關於linux調度策略的學習總結
——————————————————————————————————
這裡只做出出一些遇到問題的總結,對應線程的基礎熟悉設定等參考sun公司 的《多線程編程指南》非常詳細講述了posix線程庫。
By Water- Aug,17th.2010
——————————————————————————————————
線程的調度策略分為3個:SCHED_OTHER,SCHED_FIFO,SCHED_RR。
SCHED_OTHER是非即時分時調度策略,線程優先順序為0;
實驗結果(linux2.6 Montavista 5.0):每個線程都不能強佔其它線程,但是線程都受到時間片的限制,並不是線程不主動退出(包括被阻塞),就會一直佔用。
但是在sun公司的 《多線程編程手冊》中,其說這種情況 線程會一直佔用。
SCHED_FIFO是即時先進先出調度策略,即一當佔用CPU,除非自己阻塞或結束或有更高優先順序線程,否則會一直運行,線程優先順序為1-99;
線程會按不同的優先順序來分為不同的隊列,同優先順序的線程是按FIFO來調度的。
SCHED_RR是即時分時調度策略,其不會一直佔用CPU,運行一個時間片後會讓出CPU給自己同優先順序的線程;其實SCHED_RR與SCHED_FIFO基本是相似的,只是前者會受到時間片的限制,相同優先順序的線程,用時間片來調度。而FIFO的話,正在啟動並執行線程,是不會被其他同優先順序線程強佔的,除非自己主動退出或被阻塞。所以在採用FIFO策略時,千萬別使用一直佔用的線程(不主動退出,也沒有掛起的條件),否則其他同優先順序線程永遠不會運行了。這種情況最好使用RR策略。
指出:SCHED_OTHER是不支援優先順序使用的,而SCHED_FIFO和SCHED_RR支援優先順序的使用,他們分別為1和99,數值越大優先順序越高。即時調度策略會搶佔非即時調度策略,即只要有SCHED_FIFO或SCHED_RR這兩個即時調度策略的線程,像SCHED_OTHER的非即時調度策略的線程就不會得到運行,除非所有的即時調度策略的線程阻塞或結束。
下面測試程式thread_attr_test.c來測試線程的預設屬性;
//thread_attr_test.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sched.h>
void * thrd_fun( void * arg)
{
int my_policy;
struct sched_param my_param;
int status;
pid_t pid;
pid = getpid();
printf("my pid is %d \n",pid);
status = pthread_getschedparam ( pthread_self (), &my_policy, &my_param );
printf ("thread_routine running policy is %s,priority is %d \n", (my_policy == SCHED_FIFO ?"FIFO"
: (my_policy == SCHED_RR ?"RR"
: (my_policy == SCHED_OTHER ? "OTHER"
: " UNKOWN" ))),
my_param.sched_priority);
}
int main (int argc, char *argv[])
{
pthread_t thread_id;
pthread_attr_t thread_attr;
int thread_policy;
struct sched_param thread_param;
int status ,rr_min_priority ,rr_max_priority;
int inherit;
int detachstate;
int policy;
int scope;
struct sched_param param;
pid_t pid;
pid = getpid();
printf("main pid is %d\n",pid);
status = pthread_attr_init ( &thread_attr);
status = pthread_attr_getschedpolicy ( &thread_attr , &thread_policy);
status = pthread_attr_getschedparam ( &thread_attr, &thread_param);
printf ("default policy is %s,priority is %d \n", (thread_policy == SCHED_FIFO ?"FIFO"
: (thread_policy == SCHED_RR ?"RR"
: (thread_policy == SCHED_OTHER ? "OTHER"
: " UNKOWN" ))),
thread_param.sched_priority);
pthread_attr_getinheritsched(&thread_attr,&inherit);
if(inherit==PTHREAD_EXPLICIT_SCHED)
printf("PTHREAD_EXPLICIT_SCHED\n");
if(inherit==PTHREAD_INHERIT_SCHED)
printf("PTHREAD_INHERIT_SCHED\n");
pthread_attr_getdetachstate(&thread_attr,&detachstate);
if(detachstate==PTHREAD_CREATE_DETACHED)
printf("PTHREAD_CREATE_DETACHED\n");
if(detachstate==PTHREAD_CREATE_JOINABLE)
printf("PTHREAD_CREATE_JOINABLE\n");//非分離
pthread_attr_getschedpolicy(&thread_attr,&policy);
if(policy==SCHED_OTHER)
printf("SCHED_OTHER\n");
if(policy==SCHED_FIFO)
printf("SCHED_FIFO\n");
if(policy==SCHED_RR)
printf("SCHED_RR\n");
pthread_attr_getscope(&thread_attr,&scope);
if(scope==PTHREAD_SCOPE_SYSTEM)
printf("PTHREAD_SCOPE_SYSTEM\n");
if(scope==PTHREAD_SCOPE_PROCESS)
printf("PTHREAD_SCOPE_PROCESS\n");
pthread_attr_getschedparam(&thread_attr,¶m);
printf("priority:%d\n",param.sched_priority);
#if defined (_POSIX_THREAD_PRIORIY_SCHEDULING )
printf("supports");
#else
printf("not supports\n");
#endif
//struct sched_param param;
param.sched_priority=1;
pthread_attr_setschedpolicy(&thread_attr,SCHED_FIFO);
pthread_attr_setschedparam(&thread_attr,¶m);//設定成即時策略後必須設定優先順序,否則線程將建立失敗
pthread_attr_setinheritsched(&thread_attr,PTHREAD_EXPLICIT_SCHED); //必須把繼承屬性修改為PTHREAD_EXPLICIT_SCHED才能有效
//否則將繼續繼承父進程的策略
status = pthread_create( &thread_id ,&thread_attr, thrd_fun , NULL);
if (status != 0)
{
printf("can't creat!\n");
exit (1);
}
status = pthread_join (thread_id ,NULL);
return 0;
}
啟動並執行結果為(FC12-2.6.31.5核心):
main pid is 4318
default policy is OTHER,priority is 0
PTHREAD_INHERIT_SCHED
PTHREAD_CREATE_JOINABLE
SCHED_OTHER
PTHREAD_SCOPE_SYSTEM
priority:0
not supports
my pid is 4318
thread_routine running policy is FIFO,priority is 1
啟動並執行結果為(在arm開發板上2.4.18的核心):
main pid is 111
default policy is OTHER,priority is 0
PTHREAD_EXPLICIT_SCHED 這裡和2.6的核心屬性不同??
PTHREAD_CREATE_JOINABLE
SCHED_OTHER
PTHREAD_SCOPE_SYSTEM
priority:0
not supports
my pid is 113
thread_routine running policy is FIFO,priority is 1
從結果我們可以得出其預設屬性為:
OTHER非即時調度策略,且優先順序為0(無優先順序高低之分);
為繼承屬性(2.6) 非繼承(2.4);
為非分離屬性;
為綁定屬性,即與系統中所有線程(包括其他進程的線程)競爭。
經過pthread_attr_setschedpolicy 設定後把子線程的調度原則設定成了FIFO,且優先順序為1;所設定的優先順序範圍必須在最大和最小值之間。可以通過sched_get_priority_max和sched_get_priority_min來擷取。這程式中只是簡單的測試。
需要注意的是:
如果修改了調度策略為即時的(即FIFO或RR),則必須為其設定優先權,否則線程將建立失敗;記住要把繼承屬性修改為PTHREAD_EXPLICIT_SHED,否則子線程將繼續繼承父線程的相關策略,使得原則設定無效。
存在的問題:
有些系統需要定義_POSIX_THREAD_PRIORITY_SCHEDULING 才能設定線程的調度策略。但本次實驗中測試出系統沒有定義有些系統需要定義_POSIX_THREAD_PRIORITY_SCHEDULING 但仍然可以設定線程的調度策略,有點不明白。
調度策略的驗證:
下面對SCHED_FIFO是即時先進先出調度策略,即一當佔用CPU,除非自己阻塞或結束或有更高優先順序線程,否則會一直運行;這個策略進行驗證。
測試程式thread_shed_test.c如下:
#include <stdio.h>
#include <pthread.h>
#define FIFO_TEST
void *FunThread1(void *arg)
{
int i,j;
int policy;
struct sched_param param;
pthread_getschedparam(pthread_self(),&policy,¶m);
if(policy==SCHED_OTHER)
printf("SCHED_OTHER\n");
if(policy==SCHED_RR)
printf("SCHED_RR\n");
if(policy==SCHED_FIFO)
printf("SCHED_FIFO\n");
for(i=1;i <500;i++)
{
for(j=1;j <50000;j++)
{
}
//while(1); //調用這個來測試 FIFO時候受時間片的限制
printf("thread 1\n");
}
printf("Thread1 exit\n");
}
void* FunThread2(void *arg)
{
int i;
// for(i=1;i <5000;i++)
sched_yield();
sched_yield();
printf("Thread2 exit\n");
}
void* FunThread3(void *arg)
{
int i;
// for(i=1;i <5000;i++)
sched_yield();
sched_yield();
printf("Thread3 exit\n");
}
int main()
{
int i;
pthread_t ppid1,ppid2,ppid3;
struct sched_param param;
pthread_attr_t attr,attr2;
i=getuid();
if(i==0)
printf("The current user is root\n");
else
printf("The current user is not root\n");
param.sched_priority=1;
pthread_attr_init(&attr);
pthread_attr_init(&attr2);
pthread_attr_setschedpolicy(&attr2,SCHED_FIFO);
pthread_attr_setschedparam(&attr2,¶m);
pthread_attr_setinheritsched(&attr2,PTHREAD_EXPLICIT_SCHED); //新加,指定不繼承父線程調度策略
#ifdef FIFO_TEST
pthread_create(&ppid1,&attr2,FunThread1,NULL);
#else
pthread_create(&ppid1,NULL,FunThread1,NULL);
#endif
pthread_create(&ppid2,&attr,FunThread2,NULL);
pthread_create(&ppid3,&attr,FunThread3,NULL);
pthread_join(ppid1,NULL);
pthread_join(ppid2,NULL);
pthread_join(ppid3,NULL);
pthread_attr_destroy(&attr);
pthread_attr_destroy(&attr2);
return 0;
}
啟動並執行結果為(FC12-2.6.31.5核心):
SCHED_FIFO
thread 1
thread 1
thread 1
.
.
.
.
Thread 1
thread 1
thread 1
Thread1 exit
Thread2 exit
Thread3 exit
在main函數裡建立線程一時將其調度原則設定為SCHED_FIFO,優先順序為1,線程二和線程三的調度策略為SCHED_OTHER,優先順序為0,正確結果應該是線程一先運行結束才會輪到線程二和線程三。從運行結果來看似乎正確,但請看後文;
若改為用OTHER方式(注釋掉‘#define FIFO_TEST’ )則結果為:
SCHED_OTHER
thread 1
thread 1
thread 1
.
.
.
.
Thread 1
Thread2 exit
thread 1
thread 1
.
.
.
.
thread 1
thread 1
Thread3 exit
thread 1
thread 1
.
.
.
.
thread 1
thread 1
Thread1 exit
這種情況下,是時分調度的,3個線程都有時間片限制,時間片到則被其它線程代替。結果驗證時正確的。
但是是否線程1(FIFO策略)的只要自己不退讓或阻塞就不會被線程2或3 強佔了??
在FunThread1()中加入while(1)死迴圈後再來測試
運行結果為:(FC12-2.6.31.5核心):
The current user is root
SCHED_FIFO
Thread2 exit
Thread3 exit
--
發現FIFO的線程1,在運行一段時間後線程2和3還是得以執行了。這證明在該系統上即使是FIFO的線程,其也受到了一個時間片的限制,在系統規定的時間片到時,其必須讓出給其它線程執行。否則pc上的FC12這個系統將不能做其它的事情了(顯然不可以)。但是這時候也可以明顯的發現,此時PC機上的FC12系統對其它的操作響應(如滑鼠等)已經很慢了。這證明這個所謂的時間片應該比較長。
但是把這個程式在arm開發板上運行:
啟動並執行結果為(在arm開發板上2.4.18的核心):
The current user is root
SCHED_FIFO
------
結果顯示與FIFO的理論完全相符合,即即一當佔用CPU,除非自己阻塞或結束或有更高優先順序線程,否則會一直運行。
兩種平台上的不同的原因猜想:
可能在PC機上的FC12加入了時間片的限制,來時這個系統不容易崩潰。而ARM開發板上的linux則沒有做相關處理。