《Linux內核分析》讀書筆記(四章)

《Linux內核分析》讀書筆記(四章)

標籤(空格分隔): 20135328陳都

第四章 進程調度

調度程序負責決定將哪一個進程投入運行,什麼時候運行以及運行多長時間,進程調度程序可看作在可運行態進程之間分配有限的處理器時間資源的內核子系統。linux

4.1 多任務

多任務操做系統就是能同時併發地交互執行多個進程的操做系統。算法

多任務系統能夠劃分爲兩類:緩存

  • 非搶佔式多任務
    進程會一直執行直到本身主動中止運行
  • 搶佔式多任務
    Linux/Unix使用的是搶佔式的方式;強制的掛起進程的動做就叫作搶佔。

像全部unix的變體和許多其餘現代操做系統同樣,Linux提供了搶佔式的多任務模式。安全

進程的時間片:進程在被搶佔以前可以運行的時間是預先設置好的。服務器

4.2 Linux 的進程調度36

  • 從1991年Linux的第1版到後來的2.4內核系列,Linux的調度程序都至關簡陋,設計近乎原始,固然它很容易理解,可是它在衆多可運行進程或者多處理器的環境下都難以勝任,正由於如此,在Linux 2.5開發系列的內核中,調度程序作了大手術,開始採用了一種叫作O(1)調度程序的新調度程序——它是由於其算法的行爲而得名的。併發

  • 它解決了先前版本Linux調度程序的許多不足,引入了許多強大的新特性和性能特徵,這裏主要要感謝靜態時間片算法和針對每一處理器的運行隊列,它們幫助咱們擺脫了先前調度程序設計上的限制。模塊化

  • O(1)調度器雖然在擁有數以十計(不是數以百計)的多處理器的環境下尚能表現出近乎完美的性能和可擴展性,可是時間證實該調度算法對於調度那些響應時間敏感的程序卻有一些先天不足,這些程序咱們稱其爲交互進程一它無疑包括了全部須要用戶交互的程序,正由於如此,O(1)調度程序雖然對於大服務器的工做負載很理想,可是在有不少交互程序要運行的桌面系統上則表現不佳,由於其缺乏交互進程,自2.6內核系統開發初期,開發人員爲了提升對交互程序的調度性能引入了新的進程調度算法,其中最爲著名的是「反轉樓梯最後期限調度算法,該算法吸收了隊列理論,將公平調度的概念引入了Linux調度程序。而且最終在2.6.23內核版本中替代了O(1)調度算法,它此刻被稱爲「徹底公平調度算法」,或者簡稱CFS。函數

4.3 策略

4.3.1 I/O消耗型和處理器消耗型的進程

調度策略一般要在兩個矛盾的目標中間尋找平衡:進程響應迅速(響應時間短)和最大系統利用率(高吞吐量),爲了知足上述需求,調度程序一般採用一套很是複雜的算法來決定最值得運行的進程投入運行,可是它每每並不保證低優先級進程會被公平對待,Unⅸ系統的調度程序更傾向於I/O消耗型程序,以提供更好的程序響應速度,Linux爲了保證交互式應用和桌面系統的性能,因此對進程的響應作了優化(縮短響應想間)更傾向於優先調度I/O消耗型進程,雖然如此,但在下面你會看到,調度程序也並未忽略處理器消耗型的進程。性能

4.3.2 進程優先級

  • 調度算法中最基本的一類就是基於優先級的調度,這是一種根據進程的價值和其對處理器時間的需求來對進程分級的想法,一般作法是優先級高的進程先運行,低的後運行,相同優先級的進程按輪轉方式進行調度(一個接一個,重複進行)。
  • 在某些系統中,優先級高的進程使用的時間片也較長,調度程序老是選擇時間片未用盡並且優先級最高的進程運行,用戶和系統均可以經過設置進程的優先級來影響系統調度。
  • Linux採用了兩種不一樣的優先級範圍,第一種是用nice值,它的範圍是從―20到+19。第二種範圍是實時優先級。
    能夠用- ps-eo state,uid,pid,ppid,rtprio,time,comm.查看到你係統中的進程列衰,以及它們對應的實時優先級(位於RTPRIO 列下〉,其中若是有進
    程對應列顯示「·」,則說明它不是實時進程。

4.3.3 時間片

時間片e是一個數值,它代表進程在被搶佔前所能持續運行的時間。調度策略必須規定一個默認的時間片,但這並非件簡單的事,時間片過長會致使系統對交互的響應表現欠佳,讓人以爲系統沒法併發執行應用程序:時間片過短會明顯增大進程切換帶來的處理器耗由於確定會有至關一部分系統時間用在進程切換上,而這些進程可以用來運行的時間片卻很短,此外,I/O消耗型和處理器消耗型的進程之間的矛盾在這裏也再次顯露出來:I/O消耗型不須要長的時間片,而處理器消耗型的進程則但願越長越好(好比這樣可讓它們的高速緩存命中率更高)優化

4.4 Linux調度算法

4.4.1 調度器類

Linux調度器是以模塊方式提供的,這樣作的目的是容許不一樣類型的進程能夠有針對性地選擇調度算法。這種模塊化結構被稱爲調度器類。
它容許多種不一樣的可動態添加的調度算法並存,調度屬於本身範疇的進程。
每一個調度器都有一個優先級,基礎的調度器代碼定義在sched_fair.c文件中,它會按照優先級順序遍歷調度類,擁有一個可執行進程的最高優先級的調度器類勝出,去選擇下面要執行的那一個程序。徹底公平調度(CS)是一個針對普通進程的調度類,在Linux中稱爲SCHED_NORMAL。

4.4.2 Unix 系統中的進程調度

問題中的絕大多數均可以經過對傳統Unix調度器進行改造解決,雖然這種改造修改不小,但也並不是是結構性調整,好比,將nice值呈幾何增長而非算數增長的方式解決第二個問題:採用一個新的度量機制將從nice值到時間片的映射與定時器節拍分離開來以此解決第三個問題。可是這些解決方案都回避了實質問題——即分配絕對的時間片引起的固定的切換頻率,給公平性形成了很大變數,CFS採用的方法是對時間片分配方式進行根本性的從新設計(就進程調度器而言)徹底摒棄時間片而是分配給進程一個處理器使用比重,經過這種方式,CFS的確保了進程調度中能有恆定的公平性,而將切換頻率置於不斷變更中。

4.4.3 公平調度

CFS的出發點基於一個簡單的理念:進程調度的效果應如同系統具有―個備一個理想中的完美多任務同時,咱們能夠調度給它們無限小的時間週期,因此在任何可測量週期內,咱們給予一個進程中每一個進程一樣多的運行時間。舉例來講,假如咱們有兩個運行進程,在標準unⅸ調度模型中,咱們先運行其中一個5ms,而後再運行另外一個,5ms。但它們任何一個運行時都將佔有100%的處理器。而在理想狀況下,完美的多任務處理器模型應該是這樣的:咱們能在5ms內同時運行兩個進程,它們各自使用處理器一半的能力。

4.5 Linux調度的實現

4.5.1 時間記帳

全部的調度器都必須對進程運行時間作記帳。多數Unix系統,分配一個時間片給每個進程。那麼當每次系統時鐘節拍發生時,時間片都會被減小一個節拍週期。

  • 調度器實體結構
  • 虛擬實時

4.5.2 進程選擇

在前面內容中咱們的討論中談到若存在一個完美的多任務處理器,全部可運行進程的vruntime值將一致,但事實上咱們沒有找到完美的多任務處理器,所以CFS試圖利用一個簡單的規則去均衡進程的虛擬運行時間:當CFS的須要選擇下一個運行進程時,它會挑一個具備最小vruntime的進程,這其實就是CSF調度算法的核心:選擇具備最小、vruntime的任務。那麼剩下的內容咱們就來討論究竟是如何實現選擇具備最小、vruntime值的進程。Linux中,紅黑樹稱爲rbtree,它是一個自平衡二叉搜索樹,紅黑樹是一種以樹節點形式存儲的數據,這些數據都會對應一個鍵值,咱們能夠經過這些鍵值來快速檢索節點上的數據(重要的是,經過鍵值檢索到對應節點的速度與整個樹的節點見模成指數比關係)

  • 挑選下一個任務
  • 向樹中加入進程

4.5.3 調度器入口

進程調度的主要入口點是schedule(),它定義在文件kernel/sched.c中。

4.5.4 睡眠和喚醒

休眠(被阻塞)的進程處於一個特殊的不可執行狀態,這點很是重要,若是沒有這種特殊狀態的話,調度程序就可能選出一個本不肯意被執行的進程,更糟糕的是,休眠就必須以輪詢的方式實現了,進程休眠有多種緣由,但確定都是爲了等待一些事件,事件多是一段時間從文件讀更多數據,或者是某個硬件事件,一個進程還有可能在嘗試獲取一個已被佔用的內核信號量時被迫進入休眠,休眠的一個常見緣由就是文件I/O——如進程對個文件執行了read()操做,而這須要從磁盤裏讀取,還有,進程在獲取鍵盤輸入的時候也須要等待,不管哪一種狀況,內核的操做都相同:進程把本身標記成休跳態,從可執照課樹黑樹中移出,放入等待隊列,而後調用schedule()選擇和執行―個其餘進程,喚醒的過程恰好相反:進程被設置爲可執行狀態,而後再從等待隊列中移到可晰紅黑樹中。

4.6 搶佔和上下文切換

4.6.1 用戶搶佔

在內核返回用戶空間的時候,它知道本身是安全的,由於既然它能夠繼續去執行當前進程,那麼它固然能夠再去選擇一個新的進程去執行。因此,內核不管是在中斷處理程序仍是在系統調用後返回,都會檢查need_resched標誌,若是它被設置了,那麼,內核會選擇一個其餘(更合適的進程投入運行。從中斷處理程序或系統調用返回的返回路徑都是跟體系結構相關的,在entry.S(此文件不只包含內核入口部分的程序,內核退出部分的相關代碼也在其中)文件中經過彙編語言來實現。簡而言之,用戶搶佔在如下狀況時產生:

  • 從系統調返回用戶空間時。
  • 從中斷處理程序返回用戶空間時。

4.6.2 內核搶佔

與其餘大部分的Unⅸ變體和其餘大部分的操做系統不一樣,Linux完整地支持內核搶佔,在不支持內核搶佔的內核中,內核代碼能夠一直執行,到它完成爲止,也就是說,調度程序沒有辦法在一個內核級的任務正在執行的時候從新調度——內核中的各任務是以協做方式調度的不具有搶佔性。內核代碼一直要執行到完成(返回用戶空間)或明顯的阻塞爲止,在2.6版的內核中,內核引入了搶佔能力;如今,只要從新調度是安全的,內核就能夠在任什麼時候間搶佔正在執行的任務。

4.7 實時調度策略

Linux的實時調度算法提供了―種軟實時工做方式,軟實時的含義是,內核調度進程,盡力使進程在它的限定時間到來前運行,但內核不保證總能知足這些進程的要求。相反,硬實時系統保證在必定條件下,能夠知足任何調度的要求。Linux對於實時任務的調度不作任何保證。雖然不能保證硬實時工做方式,但Linux的實時調度算法的性能仍是很不錯的。2.6版的內核能夠知足嚴格的時間要求。

4.8 與調度相關的系統調用

4.8.1 與調度策略和優先級相關的系統調用55

  • sched_setscheduler()和 sched_getscheduler()分別用於設置和獲取進程的調度策略和實時優先級。與其餘的系統調用類似,它們的實現也是由許多參數檢查、初始化和清理構成的。其實最重要的工做在於讀取或改寫進程task_struct的policy和rt_priority的值。
  • sched_setscheduler()和 sched_getscheduler()分別用於設置和獲取進程的實時優先級。這兩個系統調用獲取封裝在sched_param特殊結構體的rt_priority中。實時調度策略的的最大優先級:是MAX_ USERRT_PRIO減1。最小優先級等於1。
  • 對於―個普通的進程,nice函數能夠將給定進程的靜態優先級增長一個給定的量。只有超級用戶才能在調用它時使用負值,從而提升進程的優先級。nice函數會調用內核的set_user_nice函數,這個函數會設置進程的的task_struct的static_prio值。

    4.8.2 與處理器綁定有關的系統調用

    Linux調度程序提供強制的處理器綁定機制。也就是說,雖然它盡力經過一種軟的(或者說天然的)親和性試圖使進程儘可能在同一個處理器上運行,但它也容許用戶強制指定「這個進程不管如何都必須在這些處理器上運行」。這種強制的親和性保存在進程的一個位掩碼標誌中。該掩碼標誌的每一位對應一個系統可用的處理器,默認狀況下,全部的位都被設置。

    4.8.3 放棄處理器時間

    Linux經過sched_yield()系統調用,提供了一種讓進程顯式地將處理器時間讓給其餘等待執行進程的機制,它是經過將進程從活動隊列中(由於進程正在執行,因此它確定位於此隊列當中)移到過時隊列中實現的,由此產生的效果不只搶佔了該進程並將其放入優先級隊列的最後面,還將其放入過時隊列中—這樣能確保在一段時間內它都不會再被執行了,因爲實時進程不會過時,因此屬於例外,它們只被移動到其優先級隊列的最後面(不會放到過時隊列中)在Linux以的早期版本中,進程只會被放置到優先級隊列的末尾,放棄的時間每每不會太長,如今,應用程序甚至內核代碼在調用sched_yield()前,應該仔細考慮是否真的但願放棄處理器時間。內核代碼爲了方便,能夠直接調用sched_yield(),先要肯定給定進程確實處於可執行狀態,而後再調用sched_yield(),用戶空間的應用程序直接使用sched_yield()系統調用就能夠 。

    4.9 小結

    進程調度程序是內核重要的組成部分,由於運行着的進程首先在使用計算機(至少在咱們大多數人看來)然而,知足進程調度的各類須要毫不是垂手可得的:很難找到「一刀切」的算法既適合衆多的可運行進程,又具備可伸縮性,還能在調度週期和吞吐量之間求得平衡,同時還知足各類負載的需求,不過,Linux內核的新CFS調度程序儘可能知足了各個方面的需求,並以較完善的可伸縮性和新穎的方法提供了最佳的解決方案,前面的章節覆蓋了進程管理的相關內容,本章則考察了進程調度所遵循的基本原理、具體實現、調度算法以及目前Linux內核所使用的接口。

相關文章
相關標籤/搜索