1. 讓出處理器
Linux提供一個系統調用運行進程主動讓出執行權:sched_yield。進程啟動並執行好好的,為什麼需要這個函數呢?有一種情況是使用者空間線程的鎖定。如果一個線程試圖取得另一個線程所持有的鎖,則新的線程應該讓出處理器知道該鎖變為可用。使用者空間鎖沒有核心的支援,這是一個最間單、最有效率的做法。但是現在Linux線程實現引入一個使用futexes的最佳化解決方案。
另一個情況是在有處理器密集型程式可用周期性調用sched_yield,試圖將該進程對系統的衝擊減到最小。不管怎麼說,如何發送器應該是系統的事情,而不是進程自己去管。eg:
int main(){ int ret, i; ret = sched_yield(); if(ret == -1){ printf("調用sched_yield失敗!\n"); } return 0;}
那該調用核心是如何?的?2.6以前的版本sched_yield所造成的影響非常小,如果存在另一個可以啟動並執行進程,核心就切換到該進程,把進行調用的進程放在可運行進程列表的結尾處。短期內核心會對該進程進行重新調度。這樣的話可能出現“乒乓球”現象,也就是兩個程式來回運行,直到他們都運行結束。2.6版本中做了一些改變:
1.如果進程是RR,把它放到可運行進程結尾,返回。
2.否則,把它從可運行進程列表移除,放到到期進程列表,這樣在其他可運行進程時間片用完之前不會再運行該進程。
3.從可執行進程列表中找到另一個要執行的進程。
2.進程的優先順序
看過CFS中會看到進程的nice value會決定進程會運行多長時間,或者說是佔用的百分比。可以通過系統調用nice來設定、擷取進程的nice value。該值的範圍是-20~19,越低的值越高的優先順序(這個在計算虛擬時間的時候放在分母上),即時進程應該是負數,eg:
int main(){ int ret, i; ret = nice(0); printf("當前進程的nice value:%d\n", ret); ret = nice(10); printf("當前進程的nice value:%d\n", ret); return 0;}
因為ret本來就可以是-1,那麼在判斷是否系統調用失敗的時候就要綜合ret和errno。還有兩個系統調用可以更靈活地設定,getpriority可以獲得進程組、使用者的任何進程中優先順序最高的。setpriority將所指定的所有進程優先順序設定為prio,eg:
int main(){ int ret, i; ret = getpriority(PRIO_PROCESS, 0); printf("nice value:%d\n", ret); ret = setpriority(PRIO_PROCESS, 0, 10); ret = getpriority(PRIO_PROCESS, 0); printf("nice value:%d\n", ret); return 0;}
進程有在處理器上執行的優先順序,也有傳輸資料的優先順序:I/O優先順序。linux有額外的兩個系統調用可用顯示設定和取得I/O nice value,但是尚未匯出:
int ioprio_get(int which, int who);
int ioprio_set(int which, int who, int ioprio);
3、處理器親和性
Linux支援具有多個處理器的單一系統。在SMP上,系統要決定每個處理器上要運行那些程式,這裡有兩項挑戰:
1.發送器必須想辦法充分利用所有的處理器。
2.切換程式啟動並執行處理器是需要代價的。
進程會繼承父進程的處理器親和性,Linux提供兩個系統調用用於擷取和設定“硬親和性”。eg:
int main(){ int ret, i; cpu_set_t set; CPU_ZERO(&set); ret = sched_getaffinity(0, sizeof(cpu_set_t), &set); if(ret == -1) printf("調用失敗!\n"); for(i = 0; i < 10; i++){ int cpu = CPU_ISSET(i, &set); printf("cpu=%i is %s\n", i, cpu?"set":"unset"); } CPU_ZERO(&set); CPU_SET(0, &set); CPU_CLR(1, &set); ret = sched_setaffinity(0, sizeof(cpu_set_t), &set); if(ret == -1) printf("調用失敗!\n"); for(i = 0; i < 10; i++){ int cpu = CPU_ISSET(i, &set); printf("cpu=%i is %s\n", i, cpu?"set":"unset"); } return 0;}
4、Linux的調度策略與優先順序
關於Linux系統中對進程的幾種調度方法和他們的區別就不在這裡說了,這裡關注的是如何擷取、設定這些值。可以使用sched_getscheduler來擷取進程的調度策略,eg:
int main(){ int ret, i; struct sched_param sp; sp.sched_priority = 1; ret = sched_setscheduler(0, SCHED_RR, &sp); if(ret == -1) printf("sched_setscheduler failed.\n"); if(errno == EPERM) printf("Process don't the ability.\n"); ret = sched_getscheduler(0); switch(ret){ case SCHED_OTHER: printf("Policy is normal.\n"); break; case SCHED_RR: printf("Policy is round-robin.\n"); break; case SCHED_FIFO: printf("Policy is first-in, first-out.\n"); break; case -1: printf("sched_getscheduler failed.\n"); break; default: printf("Unknow policy\n"); } return 0;}
sched_getparam和sched_setparam介面可用於取得、設定一個已經設定好的策略,這裡不只是返回一個策略的ID,eg:
int main(){ int ret, i; struct sched_param sp; sp.sched_priority = 1; ret = sched_setparam(0, &sp); if(ret == -1) printf("sched_setparam error.\n"); ret = sched_getparam(0, &sp); if(ret == -1) printf("sched_getparam error.\n"); printf("our priority is %d.\n", sp.sched_priority); return 0;}
Linux提供兩個用於取得有效優先值的範圍的系統調用,分別返回最大值、最小值,eg:
int main(){ int ret, i; struct sched_param sp; ret = sched_get_priority_min(SCHED_RR); if(ret == -1) printf("sched_get_priority_min error.\n"); printf("The min nice value is %d.\n", ret); ret = sched_get_priority_max(SCHED_RR); if(ret == -1) printf("sched_get_priority_max error.\n"); printf("The mmax nice value is %d.\n", ret); return 0;}
關於時間片,這個概念可能在Linux中和傳統的在作業系統的課程中學到的還是有很大的區別的,如果感興趣的化可以看看CFS裡面的。通過sched_rr_get_interval可以取到分配給pid的時間片的長度,eg:
int main(){ int ret, i; struct timespec tp; ret = sched_rr_get_interval(0, &tp); if(ret == -1) printf("sched_rr_get_interval error.\n"); printf("The time is %ds:%ldns.\n", (int)tp.tv_sec, tp.tv_nsec); return 0;}
5、即時進程的預防措施
由於即時進程的本質,開發人員在開發和調試此類程式時應該謹慎行事,如果一個即時進程突然發脾氣,系統的反應會突然變慢。任何一個CPU密集型迴圈在一個即時程式中會繼續無止境地運行下去,只要沒有優先順序更高即時進程變成可啟動並執行。因此設計即時程式的時候要謹慎,這類程式至高無上,可用輕易托跨整個系統,下面是一些要決與注意事項:
1.因為即時進程會好用系統上一切資源,小心不要讓系統其他進程等不到處理時間。
2.迴圈可能會一直運行到結束。
3.小心忙碌等待,也就是即時進程等待一個優先順序低的進程所佔有的資源。
4.開發一個即時進程的時候,讓一個終端保持開啟狀態,以更高的優先順序來運行該即時進程,發生緊急情況終端機依然會有反應,允許你終止失控的即時進程。
5.使用chrt設定、取得即時屬性。
6.資源限制
Linux對進程加上了若干資源限制,這些限制是一個進程所能耗用的核心資源的上限。限制的類型如下:
1.RLIMIT_AS:地址空間上限。
2.RLIMIT_CORE:core檔案大小上限。
3.RLIMIT_CPU:可耗用CPU時間上限。
4.RLIMIT_DATA:資料區段與堆的上限。
5.RLIMIT_FSIZE:所能建立檔案的大小上限。
6.RLIMIT_LOCKS:檔案鎖數目上限。
7.RLIMIT_MEMLOCK:不具備CAP_SYS_IPC能力的進程最多將多少個位元組鎖進記憶體。
8.RLIMIT_MSGQUEUE:可以在訊息佇列中分配多少位元組。
9.RLIMIT_NICE:最多可以將自己的友善值調多低。
10.RLIMIT_NOFILE:檔案描述符數目的上限。
11.RLIMIT_NPROC:使用者在系統上能運行進程數目上限。
12.RLIMIT_RSS:記憶體中頁面的數目的上線。
13.RLIMIT_RTPRIO:不具備CAP_SYS_NICE能力進程所能請求的即時優先順序的上限。
14.RLIMIT_SIGPENDING:在隊列中訊號量的上限,Linux特有的限制。
15.RLIMIT_STACK:堆棧大小的上限。
這些就不多說了,到了實際用到的時候再仔細看,eg:
int main(){ int ret, i; struct rlimit rlim; rlim.rlim_cur = 32*1024*1024; rlim.rlim_max = RLIM_INFINITY; ret = setrlimit(RLIMIT_CORE, &rlim); ret = getrlimit(RLIMIT_CORE, &rlim); if(ret == -1) printf("getrlimit error.\n"); printf("RLIMIT_CORE limits: soft=%ld hard=%ld\n", rlim.rlim_cur, rlim.rlim_max); return 0;}