chapter 4 進程調度
4.1 多任務
4.2 linux的進程調度
- O(1)調度器:對大服務器的工做負載很理想,可是缺乏交互進程。
- 反轉樓梯最後期限調度算法(RSDL)
- 徹底公平調度算法(CFS)
4.3 策略
4.3.1 進程分類
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 調度器類
- Linux調度器是以模塊方式提供的(也就是調度器類),目的是容許不一樣類型的進程能夠有針對性地選擇調度算法
- 調度器類容許多種不一樣的可動態添加的調度算法並存,調度屬於本身範疇的進程;
- 調度器代碼會按照優先級順序遍歷調度類,擁有一個可執行進程的最高優先級的調度器類勝出,去選擇下面要執行的那一個程序;
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,是一個自平衡二叉搜索樹,是一種以樹節點形式存儲的數據,這些數據會對應一個鍵值,能夠經過這些鍵值來快速檢索節點上的數據,並且檢索速度與整個樹的節點規模成指數比關係。 **進程
-
挑選下一個任務:
節點鍵值是可運行進程的虛擬運行時間,進程選擇算法是【運行rbtree樹種最左邊葉子節點所表明的那個進程】,函數是picknextentity()
- 向樹中加入進程:
- 發生在進程被喚醒或者經過fork調用第一次建立進程時。
- 函數enqueueentity():更新運行時間和其餘一些統計數據,而後調用enqueueentity()。 函數enqueue_entity():進行繁重的插入工做,把數據項真正插入到紅黑樹中:
-
從樹中刪除進程
4.5.3 調度器入口
- 進程調度的主要入口點是函數schedule(),定義在kernel/sched.c中;這正是內和其餘部分用於調度進程調度器的入口
- 這一函數最重要的工做就是調用picknextstate(),依次檢查每個調度類,並從最高優先級的調度類中,選擇最高優先級進程
4.5.4 睡眠和喚醒
- 進程休眠必定是爲了等待一些事件
- 進程把本身標記成休眠狀態,從可執行紅黑樹中移除;
- 放入等待隊列——由等待某些時間發生的進程組成的鏈表,內核用wakequeuehead_t來表明等待隊列
3. 喚醒操做由函數wake_up()進行
- 它會調用函數try_to _wake_up()將進程設置爲TASK_RUNNING狀態,調用enqueue_task()將進程放入紅黑樹中
- 固然,也存在虛假喚醒進程的狀態
4.6 搶佔和上下文切換
參考資料
linux內核設計與實現