linux cfs調度器 理論模型

參考資料:《調度器筆記》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)          



轉自:http://www.cnblogs.com/openix/p/3254394.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章