參考資料:《調度器筆記》Kevin.Liu
《Linux kernel development》
《深入Linux內核架構》
version: 2.6.32.9
下文中對於紅黑樹或鏈表組織的就緒隊列,統稱爲用隊列組織的就緒隊列。
linux中用struct rq將處於ready狀態的進程組織在一起。
struct rq結構體包含cfs和rt成員,分別表示兩個就緒隊列:cfs就緒隊列用於組織就緒的普通進程(這個隊列上的進程用完全公平調度器進行調度);rt就緒隊列用於組織就緒的實時進程(該隊列上的進程用實時調度器調度)。
在多核cpu系統中,每個cpu對應一個struct rq結構體實例。
核心調度器分爲:
1、週期性調度器 schedule_tick();
週期性調度器不負責進程的切換,只是定時更新調度相關的統計信息,以備主調度器使用。
2、主調度器 schedule();
主調度器的工作是完成進程的切換,將CPU的使用權從一個進程切換到另一個進程。
調度器類:
linux內核把用於處理普通進程調度的函數用struct sched_class結構體實例fair_sched_class組織起來;
把用於處理實時進程調度的函數用struct sched_class結構體實例rt_sched_class組織起來;
把用於處理idle進程調度的函數用struct sched_class結構體實例idle_sched_class組織起來。
linux中如何決定就緒進程在就緒隊列中先後順序的模型建立衍化過程:
A. runtime公平模型
CPU的總時間按就緒進程數目等分給每個進程,每個進程在就緒隊列中的先後順序由它已享用的(runtime)決定,已享用CPU時間短的進程排在隊列的最前面,反之排在隊列的後面,調度器每次都選則隊列的最前面的進程運行。
漏洞:原有A、B、C三個進程在服務器上運行的runtime各自都等於1年,這時進程D被創建,那麼接下來的一年內,只進程D在運行,其它進程如同dead一樣……
B. min_runtime公平模型
假設系統中有A、B、C三個進程,其已運行時間爲:
---------------------|--------|----------|---------
進程 | A | B | C
---------------------|--------|----------|---------
runtime(ms) | 100 | 150 | 200
---------------------|--------|----------|---------
什麼是“公平”???
操作系統應該在“當前”,將時間公平分配給“當前”系統中的每個進程,“當前”意味着:
a)進程A、B、C在系統中經歷了100 + 150 + 200 = 450ms,它們應公平享用這段時間,即每個進程應當執行150ms。
b)D進程被創建了,那麼從現在起操作系統應該將CPU時間公平分配給這四個進程(從進程創建之時起,它就應該受到“不計其它進程的歷史”的待遇,調度器對所有此後運行的進程一視同仁)。
如果將新建進程D的runtime設置爲A、B、C中runtime最大的值顯然對進程D不公,如果設置爲它們中的最小值,那又對進程A不公。我們將進程D的runtime設置爲A、B、C中最小的那個值,這就是在runtime公平模型上改進之後得到的min_runtime公平模型。
C. weight優先級模型
不同進程具有不同的重要性,重要的進程儘量被分配多的CPU時間,不重要的進程應該分配少的CPU時間。
爲了達到這個目的,我們引入一個權重(weight)參數,即每個進程有一個權重值,進程得到的CPU時間和這個權重值成正比。
假設進程A、B的權重分別是1,2,這就意味着:A進程執行了Nms後以及B進程執行2Nms後它們應當具有相同的先後順序,即它們的runtime值基本相同。由於runtime表示進程已經運行的時間,顯然和上述表述矛盾,因此我們引入另一個參數vruntime(虛擬運行時間)代替它(vruntime僅僅是一個數值,用來作爲對進程進行排序的參考,不用來反映進程真實執行時間).
每個進程有一個vruntime值,調度器總是選擇vruntime值最小的進程使用CPU資源,並且vruntime增長的速度和weight值成反比.
設單核處理器上有新建的A、B兩個進程:
a) 初始的時候兩個進程都沒有運行,runtime都等於0
-----------------|-------------|-----------
進程 | A | B
-----------------|-------------|-----------
weight | 1 | 2
-----------------|-------------|-----------
runtime(ms) | 0 | 0
-----------------|-------------|-----------
vruntime | 0 | 0
-----------------|-------------|-----------
按vruntime進行排序 | A B
-------------------------------|-----------
b) 調度器選擇A,它運行了4ms:
----------------|----------|-----------
進程 | A | B
----------------|----------|-----------
weight | 1 | 2
----------------|----------|-----------
runtime(ms) | 4 | 0
----------------|----------|-----------
vruntime | 4 | 0
----------------|----------|-----------
按vruntime進行排序 | B A
---------------------------|-----------
c) B的vruntime最小,選擇B運行,運行了4ms:
-----------------|----------|-----------
進程 | A | B
-----------------|----------|-----------
weight | 1 | 2
-----------------|----------|-----------
runtime(ms) | 4 | 4
-----------------|----------|-----------
vruntime | 4 | 2
-----------------|----------|-----------
按vruntime進行排序 | B A
----------------------------|-----------
d) B的vruntime最小,選擇B運行,運行了4ms:
----------------|----------|-----------
進程 | A | B
----------------|----------|-----------
weight | 1 | 2
----------------|----------|-----------
runtime(ms) | 4 | 8
----------------|----------|-----------
vruntime | 4 | 4
----------------|----------|-----------
按vruntime進行排序 | A B
---------------------------|-----------
e) vruntime相同,但是A在前,選擇A運行,運行了4ms:
---------------|---------|-----------
進程 | A | B
---------------|---------|-----------
weight | 1 | 2
---------------|---------|-----------
runtime(ms) | 8 | 8
---------------|---------|-----------
vruntime | 8 | 4
---------------|---------|-----------
按vruntime進行排序 | B A
-------------------------|-----------
f) B的vruntime最小,選擇B運行,運行了4ms:
----------------|---------|-----------
進程 | A | B
----------------|---------|----------
weight | 1 | 2
----------------|---------|-----------
runtime(ms) | 8 | 12
----------------|---------|-----------
vruntime | 8 | 6
----------------|---------|-----------
按vruntime進行排序 | B A
--------------------------|-----------
D、period模型
早期調度器使用了時間片模型,例如每當4ms後(或着進程還未執行完4ms,就有特殊情況產生了,比如進程要睡眠),主調度器
schedule就會重新選擇一個vruntime值最小的進程來執行。
但是現在我們不用時間片的概念了,那麼主調度器schedule應該在什麼時候啓動並選擇一個新的進程執行呢???
引入period參數
系統設定一個period值(它表示一段時間),每個進程對應一個ideal_runtime值(稱爲理想欲運行時間),每個進程的ideal_runtime值的設定方式:所有可運行進程的ideal_runtime值的和等於period,每個進程的ideal_runtime值的大小與它的權重weight成正比。
該模型規定:每個進程每次獲得CPU使用權,最多執行它對應的ideal_runtime這樣長的時間。
注意:CPU並沒有把時間分成長度爲period的時間段,系統僅僅限定了每個進程每次執行時間不能超過它對應的ideal_time指定的時間長度。
如果period=20ms,當前系統中只有A、B、C、D四個進程,它們的weight分別爲:1、2、3、4。那麼A的ideal_runtime = 2ms,B,C,D的ideal_runtime依次爲4ms,6ms, 8ms。
a) 初始情況如下:
--------------------|-----------|----------|----------|----------
進程 | A | B | C | D
--------------------|-----------|----------|----------|----------
weight | 1 | 2 | 3 | 4
--------------------|-----------|----------|----------|----------
ideal_runtime(ms) | 2 | 4 | 6 | 8
--------------------|-----------|----------|----------|----------
runtime(ms) | 0 | 0 | 0 | 0
--------------------|-----------|----------|----------|----------
vruntime | 0 | 0 | 0 | 0
--------------------|-----------|----------|----------|----------
按vruntime進行排序 | A B C D
--------------------|--------------------------------------------
b) 和前一個模型一樣,vruntime的行走速度和權重值成反比,設定權重權爲1的A進程的vruntime和實際runtime行走速度相同。A先執行,它執行了2ms,此時:
--------------------|-----------|----------|----------|----------
進程 | A | B | C | D
--------------------|-----------|----------|----------|----------
weight | 1 | 2 | 3 | 4
--------------------|-----------|----------|----------|----------
ideal_runtime(ms) | 2 | 4 | 6 | 8
--------------------|-----------|----------|----------|----------
runtime(ms) | 2 | 0 | 0 | 0
--------------------|-----------|----------|----------|----------
vruntime | 2 | 0 | 0 | 0
--------------------|-----------|----------|----------|----------
按vruntime進行排序 | B C D A
--------------------|--------------------------------------------
c) B的vruntime值最小,選擇B運行,假設B運行了3ms:
--------------------|-----------|----------|----------|----------
進程 | A | B | C | D
--------------------|-----------|----------|----------|----------
weight | 1 | 2 | 3 | 4
--------------------|-----------|----------|----------|----------
ideal_runtime(ms) | 2 | 4 | 6 | 8
--------------------|-----------|----------|----------|----------
runtime(ms) | 2 | 3 | 0 | 0
--------------------|-----------|----------|----------|----------
vruntime | 2 | 1.5 | 0 | 0
--------------------|-----------|----------|----------|----------
按vruntime進行排序 | C D B A
--------------------|--------------------------------------------
d) C的vruntime值最小,選擇C運行,假設C運行了3ms:
--------------------|-----------|----------|----------|----------
進程 | A | B | C | D
--------------------|-----------|----------|----------|----------
weight | 1 | 2 | 3 | 4
--------------------|-----------|----------|----------|----------
ideal_runtime(ms) | 2 | 4 | 6 | 8
--------------------|-----------|----------|----------|----------
runtime(ms) | 2 | 3 | 3 | 0
--------------------|-----------|----------|----------|----------
vruntime | 2 | 1.5 | 1 | 0
--------------------|-----------|----------|----------|----------
按vruntime進行排序 | D C B A
--------------------|--------------------------------------------
e) D的vruntime值最小,選擇D運行,假設D運行了8ms:
--------------------|-----------|----------|----------|----------
進程 | A | B | C | D
--------------------|-----------|----------|----------|----------
weight | 1 | 2 | 3 | 4
--------------------|-----------|----------|----------|----------
ideal_runtime(ms) | 2 | 4 | 6 | 8
--------------------|-----------|----------|----------|----------
runtime(ms) | 2 | 3 | 3 | 8
--------------------|-----------|----------|----------|----------
vruntime | 2 | 1.5 | 1 | 2
--------------------|-----------|----------|----------|----------
按vruntime進行排序 | C B A D
--------------------|--------------------------------------------
f) 進程D運行的時間等於它的ideal_runtime,調度器被激活,重新選擇一個進程運行,接着C進程被選中執行。
關鍵在於: C可以運行多長時間???
根據ideal_runtime的定義,它只是要求,每個進程每次佔用CPU的資源不超過它對應的ideal_runtime,上次進程C被調度的時候它只執行了3ms,沒有超過它的ideal_runtime(6ms);但是,這次它又可以獲得CPU的使用權了,是新的一次調度了,與之前無關。因此,進程C最多可以運行6ms,那麼接下來進程C可以連續運行6ms:
--------------------|-----------|----------|----------|----------
進程 | A | B | C | D
--------------------|-----------|----------|----------|----------
weight | 1 | 2 | 3 | 4
--------------------|-----------|----------|----------|----------
ideal_runtime(ms) | 2 | 4 | 6 | 8
--------------------|-----------|----------|----------|----------
runtime(ms) | 2 | 3 | 9 | 8
--------------------|-----------|----------|----------|----------
vruntime | 2 | 1.5 | 3 | 2
--------------------|-----------|----------|----------|----------
按vruntime進行排序 | B A D C
--------------------|--------------------------------------------
不要錯誤地認爲:系統將CPU時間劃分成一段一段的,每片長度爲period,並且將它們按權重分配給每個進程,並且規定它們在該period內最多執行ideal_runtime限定的時間,進入下一個period時間段後,系統又重新爲各個進程分配ideal_runtime; 因爲CPU並沒有把時間分成長度爲period的時間段,系統僅僅限定了每個進程每次執行時不能超過它對應的ideal_time指定的時間長度。
該機制的作用是:每個進程在就緒隊列中等地的時間不會超過period,因爲每個進程獲得CPU使用權後,如果它執行的時間等於它的ideal_runtime,那麼它的vruntime基本上就比其它所有進程的vruntime值高了,自然會排到隊列的後面。
上述基於weight優先級模型和period模型的調度器所實現的效果, 每個進程每次調度運行的時間不在受4ms(例如)的限制了,而是可以運行“任意”長時間:
a) 每個進程每次獲得CPU使用權最多可以執行與它對應的ideal_runtime那麼長的時間。
b) 如果每個進程每次獲得CPU使用權時它都執行了它對應的ideal_runtime那麼長的時間,整個就緒隊列的順序保持不變。
c) 如果某個進程某幾次獲得CPU使用權時運行的時間小於它ideal_time指定的時間(即它被調度時沒有享用完它可以享用的最大
時間),按照vruntime進行排序的機制會使得它儘量排在隊列的前面,讓它儘快把沒有享用完的CPU時間彌補起來。
period抽象模型基本上就是對內核cfs調度機制的一個抽象(沒有考慮睡眠,搶佔等細節):
a) 每個進程有一個權重值(weight),值越大,表示該進程越優先。
b) 每個進程還對應一個vruntime(虛擬時間),它是根據進程實際運行的時間runtime計算出來的。vruntime值不能反映進程執行的真實時間,只是用來作爲系統判斷接下來應該選擇哪個進程使用CPU的依據————調度器總是選擇vruntime值最小的進程執行。
c) vruntime行走的速度和進程的weight成反比。
d) 爲了保證在某段時間(period)內每個進程至少能執行一次,操作系統引入了ideal_runtime的概念,規定每次獲得CPU使用權時,執行時間不能超過它對應的ideal_runtime值。達到該值就會激活調度器,讓調度器再選擇一個vruntime值最小的進程執行。
e) 每個進程的ideal_runtime長度與它的weight成正比,如果有N個進程,那麼:
task[i]->weight
task[i]->ideal_time = -------------------- * period
sum_weight(task, N)