進程調(diào)度
調(diào)度程序復(fù)雜決定哪個進程投入運行,合適運行以及運行時長。調(diào)度程序可看做在可運行態(tài)進程之間分配有限的處理器時間資源內(nèi)核子系統(tǒng)。只有通過調(diào)度程序的合理調(diào)度,系統(tǒng)資源才能最大程度地發(fā)揮作用,多進程才會有并發(fā)執(zhí)行的效果。
1.多任務(wù)
多任務(wù)就是操作系統(tǒng)能同時并發(fā)地交互執(zhí)行多個進程。實際上是使多個任務(wù)處于阻塞或睡眠狀態(tài),這些任務(wù)位于內(nèi)存,但是并不處于可運行狀態(tài),知道某一時間發(fā)生。Linux系統(tǒng)中往往有多個程序在內(nèi)存,但是僅有一個處于可運行狀態(tài)。
多任務(wù)系統(tǒng)劃分為兩類:搶占式和非搶占式。Linux采用搶占式多任務(wù)模式,進程在被搶占之前能夠運行一定時間,這個時間稱為時間片(timeslice)。從全局角度來看,合理調(diào)整時間片可以優(yōu)化系統(tǒng)資源的使用效率。
2.調(diào)度策略
調(diào)度策略決定進程何時運行以及處理器使用時間,因此調(diào)度器的策略往往決定系統(tǒng)的整體印象。
I/O消耗型和處理器消耗型進程
進程分為I/O消耗型,處理器消耗型,以及混合型。I/O消耗型大部分時間用于等待或提交I/O請求,因此這種類型特點就是頻繁需要處于運行態(tài),但是運行時間很短,例如用戶圖形界面;處理器消耗型,很少需要I/O,主要是占用大量CPU時間,用于計算,例如Matlab;混合型,例如字處理器,通常等待鍵盤輸入,同時大量占用CPU進行拼寫檢查和宏計算。
針對不同類型的進程需要不同的調(diào)度策略,考慮進程的主要需求是響應(yīng)迅速還是高吞吐量。
進程優(yōu)先級
Linux采用兩種不同的優(yōu)先級范圍:nice
值和實時優(yōu)先級
。nice
值越小表明優(yōu)先級越高,實時優(yōu)先級
正好相反(值越大優(yōu)先級越高),同時實時性進程的優(yōu)先級高于非實時性進程。
時間片
前面說了時間片是進程被搶占前所能持續(xù)運行的時間,在Linux中,時間片不是一個定值,而是根據(jù)nice
值分配處理器的使用比例,所以時間片跟系統(tǒng)負載相關(guān)。nice
值越大的進程,時間片就越小。
對于Linux中使用的CFS調(diào)度器,其搶占時機取決于新的可運行程序消耗了多少處理器使用比。若消耗的使用比比當(dāng)前進程小,則搶占處理器立刻投入運行。否則,推遲其運行。
3.Linux調(diào)度的實現(xiàn)
Linux的CFS調(diào)度算法實現(xiàn)主要有4個組成部分:
- 時間記賬
- 進程旋轉(zhuǎn)
- 調(diào)度器入口
- 睡眠和喚醒
時間記賬
所有調(diào)度器必須對運行時間記賬,每次系統(tǒng)時鐘節(jié)拍發(fā)生時,分配給進程的時間片減少一個節(jié)拍,當(dāng)節(jié)拍為0時,則會被另一個時間片尚未減到0的進程搶占。Linux中使用一個調(diào)度器實體結(jié)構(gòu)——sched_entity
來記錄,該實體嵌入在進程描述符task_struct
內(nèi)。同時CFS采用虛擬實時vruntime
來度量進程的運行時間,實現(xiàn)記賬功能。
進程選擇
CFS在選擇下一個運行進程時,會挑選一個vruntime
最小的進程。CFS使用紅黑樹(請翻閱數(shù)據(jù)結(jié)構(gòu),或者直接看做一種高效的數(shù)據(jù)結(jié)構(gòu),查找和刪除效率很高,O(log(n))的復(fù)雜度)來存儲可運行進程隊列,因此每次從紅黑樹中選擇最左邊的節(jié)點。
調(diào)度器入口
進程調(diào)度的主要入口是函數(shù)schedule()
,它是內(nèi)核其他部分用于調(diào)用進程調(diào)度器的入口:選擇哪個進程可以運行,何時將其投入運行。通常schedule()
都需要和一個具體的調(diào)度類相關(guān)聯(lián),也就是說,它會找到一個最高優(yōu)先級的調(diào)度類——后者需要有自己的可運行隊列,然后詢問后者誰是下一個該運行的進程。
睡眠和喚醒
休眠(被阻塞)的進程處于特殊的不可執(zhí)行狀態(tài),往往是等待某事件或者I/O資源。內(nèi)核對其操作:進程標(biāo)記自己為休眠狀態(tài),從可執(zhí)行紅黑樹中移除,放入等待隊列,然后調(diào)用schedule()
選擇和執(zhí)行其他一個進程。
喚醒過程剛好相反:進程被設(shè)置為可執(zhí)行狀態(tài),然后從等待隊列中移到可執(zhí)行紅黑樹中。
4.搶占和上下文切換
上下文切換,就是從一個可執(zhí)行進程切換到另一個可執(zhí)行進程,由context_switch()
函數(shù)負責(zé)處理。每當(dāng)一個新進程準(zhǔn)備投入運行的時候,schedule()
都會調(diào)用該函數(shù),其主要完成兩個工作:
- 調(diào)用
switch_mm()
,負責(zé)把虛擬內(nèi)存從上一個進程映射切換到新進程中 - 調(diào)用
switch_to()
,負責(zé)從上一個進程的處理器狀態(tài)切換到新進程的處理器狀態(tài)。包括保存、恢復(fù)棧信息和寄存器信息,還有其他與體系結(jié)構(gòu)相關(guān)的狀態(tài)信息,都必須以每個進程為對象進行管理和保存。
內(nèi)核還必須知道何時調(diào)用schedule()
,內(nèi)核提供了一個need_resched
標(biāo)志來表明是否需要重新執(zhí)行一次調(diào)度。在某個進程被搶占,優(yōu)先級高的程序進入執(zhí)行狀態(tài),返回用戶空間以及中斷返回時,內(nèi)核會檢查need_resched
標(biāo)志。每個進程都有need_resched標(biāo)志,這是因為訪問進程描述符內(nèi)數(shù)值快于全局變量。
用戶搶占
用戶搶占發(fā)生在:從系統(tǒng)調(diào)用返回用戶空間時和從中斷處理程序返回用戶空間時。
內(nèi)核搶占
Linux支持內(nèi)核搶占,在重新調(diào)度是安全的時候(沒有持有鎖,鎖是非搶占區(qū)域的標(biāo)志),內(nèi)核就可以在任何時間搶占正在執(zhí)行的任務(wù)。內(nèi)核搶占發(fā)生在:
- 中斷處理程序正在執(zhí)行,且返回內(nèi)核空間之前
- 內(nèi)核代碼再一次具有可搶占性的時候
- 內(nèi)核中任務(wù)顯示調(diào)用
schedule()
- 內(nèi)核中任務(wù)阻塞
5.實時調(diào)度策略
Linux提供兩種實時調(diào)度策略:FIFO(先入先出)和RR(輪詢)兩種方式。FIFO是指先到來的任務(wù)占用處理器,直到其主動放棄,再讓第二個到來的任務(wù)占用處理器。RR與FIFO類似,但是任務(wù)耗盡其時間片后,必須由下一個任務(wù)占用處理器。
小結(jié)
本節(jié)內(nèi)容,其實對于本人而言可能更為簡單一些(因為研究生期間接觸了一些無線通信的調(diào)度算法),因此省略了一些細節(jié),我自己也是喜歡先了解大概流程,再回頭細讀的方式學(xué)習(xí),不然容易被細節(jié)阻礙思路。