今天讓我們來一起了解一下linux cpu的進程調度,對於linux服務器,通常會碰到2個問題
1、實時性,有什麼辦法能確保某一個進程能優先運行、並且不受時間片的限制,只有等待它運行完了,其它進程才能運行?
2、多核cpu,有什麼辦法能夠自定義,綁定某些進程在某些cpu上?
那麼在探討這倆問題之前,先來了解一下linux進程調度基礎知識。
多任務系統分為2類。
非搶占式多任務:除非進程自己主動停止運行,否則它會一直執行;
搶占式多任務:有調度程序來決定什麼時候停止某一進程的運行,以便其它進程能夠得到執行機會。linux采用的是此種方式。
進程對於處理的使用上,也分為2類:
I/O消耗型:有大量的磁盤、網絡io操作,這種進程,其大部分時間都堵塞在io請求及其響應上;
處理器消耗型:這種程序大部分是算法很復雜,一個極端的例子就是while(1),死循環。
進程優先級:
高優先級的進程,先運行,並且其享用的時間片較長。低優先級進程則反之。
時間片:
過大、等待時間長,過小、進程切換頻繁。默認時間片20ms。
調度的公平性
在支持多進程的系統中,理想情況下,各個進程應該是根據其優先級公平地占有CPU。而不會出現“誰運氣好誰占得多”這樣的不可控的情況。
linux實現公平調度基本上是兩種思路:
1、給處於可執行狀態的進程分配時間片(按照優先級),用完時間片的進程被放到“過期隊列”中。等可執行狀態的進程都過期了,再重新分配時間片;
2、動態調整進程的優先級。隨著進程在CPU上運行,其優先級被不斷調低,以便其他優先級較低的進程得到運行機會;
後一種方式有更小的調度粒度,並且將“公平性”與“動態調整優先級”兩件事情合二為一,大大簡化了內核調度程序的代碼。因此,這種方式也成為內核調度程序的新寵。
強調一下,以上兩點都是僅針對普通進程的。而對於實時進程,內核既不能自作多情地去動態調整優先級,也沒有什麼公平性可言。
一個有趣的例子:
一個系統,2個進程,一個文字編輯、一個視頻編碼。前者是I/O消耗型,後者處理器消耗型。那麼處理器在對待這兩種進程是如何分配優先級和時間片的呢。
首先,文字編輯,其大部分時間都在I/O等待上,需要對用戶的請求及時響應,所以其優先級高,並且時間片長。當有用戶請求時,會中斷視頻編碼的運行。當需要等待I/O響應時,會及時交出時間片,給視頻編碼用。
相反,視頻編碼,優先級低,時間片短。
===========================================================================
好,基本知識介紹完了,下面來回答開篇提的2個問題。
linux兩種實時調度策略:
SCHED_NORMAL:普通調度策略,平時我們所使用,基於時間片的搶占式調度策略。
SCHED_FIFO:先入先出調度,不使用時間片,一旦一個SCHED_FIFO級進程處於可執行狀態,就會一直執行下去,直到它自己受阻塞或顯式地釋放cpu為止。FIFO比NORMAL優先級高,只有較高優先級的SCHED_FIFO或SCHED_RR任務才能搶占SCHED_FIFO任務。
SCHED_RR:和SCHED_FIFO類似,但是使用時間片,其優先級比SCHED_NORMAL高。當SCHED_RR時間片耗盡,相同優先級的其它實時進程會被輪流調度,注意是相同優先級的實時進程。換句話說,當其時間片耗盡,只有相同優先級的SCHED_FIFO和SCHED_RR可以被cpu調度,而低優先級的SCHED_NORMAL是不會被輪流到的。當然高優先級的實時進程,可以搶占。
實時優先級范圍0~99,99是最高優先級。可以通過函數sched_get_priority_max獲取。
總結:實時調度,可以使用SCHED_FIFO和SCHED_RR,區別是前者沒有時間片,知道其運行完畢或受阻塞,後者有時間片,時間片耗盡,cpu可以交給同優先級的實時任務使用。
說了一大堆,到底如何設置實時調度策略,函數有哪些呢?
nice() //設置進程的nice值 sched_setscheduler()//設置進程的調度策略 sched_getscheduler()//獲取進程的調度策略 sched_setparam()//設置進程的實時優先級 sched_getparam()//獲取進程的實時優先級 sched_get_priority_max()//獲取實時優先級最大值 sched_get_priority_min()//獲取實時優先級最小值 sched_rr_get_interval()//獲取進程時間片 sched_setaffinity()//設置進程處理器的親和力 sched_getaffinity()//獲取進程處理器的親和力 sched_yield()//暫時讓出處理器
設置實時調度策略SCHED_FIFO例子:
int rtsched_set_my_realtime_priority (int32_t prio) { struct sched_param schp; if (sched_getparam(0, &schp) < 0) { return(-1); } schp.sched_priority = prio; if (sched_setscheduler(0, SCHED_FIFO, &schp) < 0) { return(-1); } return(1); }
int rtsched_setaffinity_by_name(int32_t cpuid) { cpu_set_t mask; CPU_ZERO(&mask); CPU_SET(cpuid, &mask); sched_setaffinity(0, sizeof(cpu_set_t), &mask); return 1; }這是一個綁定cpu的例子,系統默認進程可以在任何一個cpu上運行,但為了保證某些進程的實時性,把它綁定在某個空閒cpu上運行也是很有必要的。
放棄cpu
好了,綁定cpu,設定優先級,都保證了某個進程的實時性,那麼如果我們想暫時放棄其實時性,讓其讓出cpu,讓別的進程運行一會,有什麼辦法呢?
可以調用函數sched_yield(),其將進程從活動隊列移到過期隊列中,交出其占用的cpu,需要注意的是,對於實時進程,不是將其放倒過期隊列中,而是放到優先級隊列的最後面。而不會放到過期隊列中去。
需要注意的地方:
1、最好優先級,千萬別隨便設置,一旦設置其他進程就沒得玩了,最高99,設個98就已經很高了,作者曾經試驗過,一旦設置99,連ssh都連不上了,囧。。。。。。。;
2、對於cpu的綁定和優先級的設定,是可以針對線程的,O(∩_∩)O~;
3、高優先級,則代表不去釋放cpu,假設有這樣一種情況,pthread1、pthread2都綁定在cpu1上,並且都是實時同優先級的線程。1獲取到spin_lock,然後io阻塞交出cpu給2,恰巧2和1共享同一資源,也要去spin_lock同一資源,好吧,想想看,會是什麼結局,2會一直spin_lock,占用cpu,而1又獲取不到cpu,這2位就在這僵著,誰也無法繼續執行。囧。。。。。。。。。