20135239 益西拉姆 linux內核分析 讀書筆記之第四章

chapter 4 進程調度

4.1 多任務

  • 多任務操做系統就是能同時併發的交互執行多個進程的操做系統。
  • 多任務系統能夠劃分爲兩類:linux

    - 非搶佔式多任務:
            - 進程會一直執行直到本身主動中止運行(這一步驟稱爲讓步)
     - 搶佔式多任務:
          - Linux/Unix使用的是搶佔式的方式;強制的掛起進程的動做就叫作搶佔。進程在被搶佔以前可以運行的時間是預先設置好的(也就是進程的時間片)

4.2 linux的進程調度

  1. O(1)調度器:對大服務器的工做負載很理想,可是缺乏交互進程。
  2. 反轉樓梯最後期限調度算法(RSDL)
  3. 徹底公平調度算法(CFS)

4.3 策略

  • 策略決定調度程序在合適讓什麼程序運行。

4.3.1 進程分類

  • I/O消耗型
    • 進程的大部分時間用來提交I/O請求或者等待I/O請求,常常處於可運行狀態可是運行時間很短,等待更多的請求時最後總會阻塞。
  • 處理器消耗型算法

    • 把時間大多用在執行代碼上,除非被搶佔,不然一般都會不停運行。
  • 調度策略一般要在兩個矛盾的目標中間尋找平衡:服務器

    • 進程調度迅速(響應時間短)
    • 最大系統利用率(高吞吐量)
  • Linux傾向於優先調度I/O消耗型進程併發

4.3.2 進程優先級

  • 調度算法中最基本的一類就是基於優先級的調度:
    • 優先極高的進程先運行;相同優先級的進程按照輪轉方式進行調度
  • 調度程序老是選擇時間片未用盡並且優先級最高的進程運行。
  • Linux採用了兩種不一樣的優先級範圍:
    • nice值(從-20——+19):默認值爲0;數值越大意味着優先級越低;能夠經過 ps-el查看系統進程列表並找到NI標記列對應的優先級
    • 實時優先級(從0——99):越高的實時優先級級數意味着進程優先級越高
    • 兩者互不交互

4.3.3 時間片

  • 時間片表示進程在被搶佔前所能持續運行的時間。
  • 調度策略必須肯定一個默認的時間片;
  • Linux的CFS調度器並無直接劃分時間片到進程,而是將處理器的使用比例劃分給了進程。也就是說,其搶佔時機取決於新的可執行程序消耗了多少處理器使用比,若是消耗的使用比比當前進程小,則新進程當即投入運行搶佔當前進程。

4.3.4 調度策略的活動

4.4 linux調度算法

4.4.1 調度器類

  1. Linux調度器是以模塊方式提供的(也就是調度器類),目的是容許不一樣類型的進程能夠有針對性地選擇調度算法
  2. 調度器類容許多種不一樣的可動態添加的調度算法並存,調度屬於本身範疇的進程;
  3. 調度器代碼會按照優先級順序遍歷調度類,擁有一個可執行進程的最高優先級的調度器類勝出,去選擇下面要執行的那一個程序;

4.4.2 Unix系統中的進程調度

Unix使用的調度算法是分配絕對的時間片,這樣就會引起固定的切換頻率,不利於公平性。 而Linux採用的CFS徹底摒棄了時間片,分配給進程一個處理器使用比重,保證恆定的公平性和變更的切換頻率。函數

4.4.3 公平調度(CFS)

  • CFS的出發點基於一個簡單的理念:進程調度的效果應當如同系統具有一個理想中的完美任務處理器。
  • CFS的作法以下:操作系統

    • 容許每一個進程運行一段時間、循環輪轉、選擇運行最少的進程做爲下一個運行進程;
    • nice值做爲進程得到的處理器運行比的權重 即:絕對的nice值再也不影響調度決策,它們的相對值纔會影響處理器時間的分配比例——幾何加權。
    • 每一個進程都按照其權重在所有的可運行進程中所佔的比例對應的「時間片」來運行
  • 目標延遲:無限小調度週期的近似值設計

  • 最小粒度:每一個進程得到的時間片底線,默認爲1ms。
  • 沒有時間片概念可是仍需維持時間記帳。

4.5 linux調度的實現

——即CFS調度算法的實現。code

四個組成部分:隊列

- 時間記帳
- 進程選擇
- 調度器入口
- 睡眠和喚醒

4.5.1 時間記帳

  • 全部的調度器都必須對進程的運行時間作記帳;
  • CFS使用調度器實體結構來追蹤運行記帳
  • 1.調度器實體結構
    • CFS使用調度器實體結構來追蹤進程運行記帳:
  • 2.虛擬實時
    • CFS使用了vruntime變量來存放進程的虛擬運行時間,用來表示進程到底運行了多少時間,以及它還應該運行多久。
    • 這個虛擬運行時間是加權的,與定時器節拍無關。
    • 虛擬運行時間以ns爲單位。 
    • 相關的函數是updatecurr(),它計算了當前進程的執行時間並存放入變量deltaexec中,而後又將運行時間傳遞給_updatecurr();
    • _updatecurr()根據當前可運行進程總數對進行時間進行加權計算,最終將權重值與當前運行進程的vruntime值相加。 

4.5.2 進程選擇

  • CFS算法核心:選擇具備最小vrntime的任務
  • 具體作法:利用紅黑樹rbtree(以節點形式存儲數據的二叉樹)
  • 舉例:
    • 選擇下一個任務:從根節點中序遍歷二叉樹,一直到葉子節點(也就是vrntime最小的進程);
    • 向樹中加入進程:在進程變爲可執行狀態或者經過fork()調用第一次建立進程;
    • 從樹中刪除進程:發生在進程阻塞或者終止的時候

**Linux中,紅黑樹被稱爲rbtree,是一個自平衡二叉搜索樹,是一種以樹節點形式存儲的數據,這些數據會對應一個鍵值,能夠經過這些鍵值來快速檢索節點上的數據,並且檢索速度與整個樹的節點規模成指數比關係。 **進程

  1. 挑選下一個任務:

    節點鍵值是可運行進程的虛擬運行時間,進程選擇算法是【運行rbtree樹種最左邊葉子節點所表明的那個進程】,函數是picknextentity() 

  2. 向樹中加入進程:
    • 發生在進程被喚醒或者經過fork調用第一次建立進程時。
    • 函數enqueueentity():更新運行時間和其餘一些統計數據,而後調用enqueueentity()。 函數enqueue_entity():進行繁重的插入工做,把數據項真正插入到紅黑樹中: 
  3. 從樹中刪除進程

    • 刪除動做發生在進程堵塞或終止時。

    • 相關函數是dequeueentity()和dequeueentity(): 

4.5.3 調度器入口

  1. 進程調度的主要入口點是函數schedule(),定義在kernel/sched.c中;這正是內和其餘部分用於調度進程調度器的入口
  2. 這一函數最重要的工做就是調用picknextstate(),依次檢查每個調度類,並從最高優先級的調度類中,選擇最高優先級進程

4.5.4 睡眠和喚醒

  • 進程休眠必定是爲了等待一些事件
    • 進程把本身標記成休眠狀態,從可執行紅黑樹中移除;
    • 放入等待隊列——由等待某些時間發生的進程組成的鏈表,內核用wakequeuehead_t來表明等待隊列

3. 喚醒操做由函數wake_up()進行

- 它會調用函數try_to _wake_up()將進程設置爲TASK_RUNNING狀態,調用enqueue_task()將進程放入紅黑樹中
- 固然,也存在虛假喚醒進程的狀態

4.6 搶佔和上下文切換

  • 上下文切換由定義在kernel/sched.c中的context_switch()函數負責,每當一個新的進程被選出來準備運行的時候,schedule()就會調用該函數:

    • 調用switch_mm(),負責把虛擬內存從上一個進程映射切換到新的進程中;
    • 調用switch_to(),負責從上一個進程的處理器狀態切換到新進程的處理器狀態
  • Linux系統支持內核搶佔

    • 只要沒有鎖,內核就能夠進程搶佔;
    • 爲了支持搶佔,每一個進程的threadinfo都加入了preemptcount計數器(初值爲0,每當使用鎖的時候就加1,釋放鎖的時候數值減1),當數值爲0的時候,內核就能夠搶佔
  • 內核搶佔發生在:
    • 中斷處理程序正在執行且返回內核空間以前;
    • 內核代碼再一次具備可搶佔性的時候;
    • 內核中的任務顯式地調用schedule函數

參考資料

linux內核設計與實現

相關文章
相關標籤/搜索