【學習時間:1小時45分 撰寫博客時間:2小時10分鐘】算法
【學習內容:Linux的進程調度實現、搶佔和上下文切換、與調度相關的系統調用】安全
調度程序負責決定將哪一個進程投入運行,什麼時候運行以及運行多長時間。進程調度程序可看作在可運行態進程之間分配有限的處理器時間資源的內核子系統。服務器
最大限度利用處理器時間的原則:只要有能夠執行的進程,那麼總會有程序正在執行。網絡
1.概念:多任務操做系統就是能同時併發地交互執行多個進程的操做系統,在單處理器機器上這會產生多個進程在同時運行的幻覺,在多處理器機器上,這會使多個進程在不一樣的處理機上真正同時、並行地運行。併發
2. 分類:多任務系統能夠劃分爲兩類函數
在Linux 2.5開發系列的內核中,調度程序作了大手術,開始採用了一種叫作O(1)調度程序的新調度程序——它是由於其算法的行爲而得名的。性能
策略決定調度程序在什麼時候讓什麼進程運行。學習
1. I/O消耗型進程優化
2. 處理器耗費型spa
3. 調度策略一般要在兩個矛盾的目標中間尋找平衡:進程響應迅速(響應時間短)和最大系統利用率(高吞吐量),爲了知足上述需求,調度程序一般採用一套很是複雜的算法來決定最值得運行的進程投入運行,可是它每每並不保證低優先級進程會被公平對待,Unⅸ系統的調度程序更傾向於I/O消耗型程序,以提供更好的程序響應速度,Linux爲了保證交互式應用和桌面系統的性能,因此對進程的響應作了優化(縮短響應想間)更傾向於優先調度I/O消耗型進程,雖然如此,調度程序也並未忽略處理器消耗型的進程。
1. 基於優先級的調度:優先極高的進程先運行,相同優先級的進程按照輪轉方式進行調度。
2. 優先級分爲兩類:
注:兩者互不交互。
CFS採用的方法是對時間片分配方式進行根本性的從新設計(就進程調度器而言)徹底摒棄時間片而是分配給進程一個處理器使用比重,經過這種方式,CFS的確保了進程調度中能有恆定的公平性,而將切換頻率置於不斷變更中。
CFS基於一個簡單的理念:進程調度的效果應當如同系統具有一個理想中的完美任務處理器。CFS的作法以下:
在理想狀況下,完美的多任務處理器模型應該是這樣的:咱們能在5ms內同時運行兩個進程,它們各自使用處理器一半的能力。
CFS相關代碼位於kernel/sched_fair.c中。它有四個組成部分:
全部的調度器都必須對進程運行時間作記帳。多數Unix系統,分配一個時間片給每個進程。那麼當每次系統時鐘節拍發生時,時間片都會被減小一個節拍週期。
1. 調度器實體結構
調度器實體結構做爲一個名爲se的成員變量,嵌入在進程描述符struct task_ struct內。
2. 虛擬實時
1. CFS算法核心:選擇具備最小vrntime的任務
2. 具體作法:利用紅黑樹rbtree(以節點形式存儲數據的二叉樹)
3. 舉例:
1. 等待隊列:休眠經過等待隊列進行處理,等待隊列是由某些事件發生的進程組成的簡單鏈表。
2. 喚醒
喚醒操做由函數wake_ up()進行:
上下文切換,就是從一個可執行進程切換到另外一個可執行進程,由定義在kernel/schedule.c中的context_ switch()函數負責處理。完成了兩項工做:
在內核返回用戶空間的時候,它知道本身是安全的,由於既然它能夠繼續去執行當前進程,那麼它固然能夠再去選擇一個新的進程去執行。因此,內核不管是在中斷處理程序仍是在系統調用後返回,都會檢查need_resched標誌,若是它被設置了,那麼,內核會選擇一個其餘(更合適的進程投入運行。從中斷處理程序或系統調用返回的返回路徑都是跟體系結構相關的,在entry.S(此文件不只包含內核入口部分的程序,內核退出部分的相關代碼也在其中)文件中經過彙編語言來實現。簡而言之,用戶搶佔在如下狀況時產生:
與其餘大部分的Unⅸ變體和其餘大部分的操做系統不一樣,Linux完整地支持內核搶佔,在不支持內核搶佔的內核中,內核代碼能夠一直執行,到它完成爲止,也就是說,調度程序沒有辦法在一個內核級的任務正在執行的時候從新調度——內核中的各任務是以協做方式調度的不具有搶佔性。內核代碼一直要執行到完成(返回用戶空間)或明顯的阻塞爲止,在2.6版的內核中,內核引入了搶佔能力;如今,只要從新調度是安全的,內核就能夠在任什麼時候間搶佔正在執行的任務。
Linux的實時調度算法提供了―種軟實時工做方式,軟實時的含義是,內核調度進程,盡力使進程在它的限定時間到來前運行,但內核不保證總能知足這些進程的要求。相反,硬實時系統保證在必定條件下,能夠知足任何調度的要求。Linux對於實時任務的調度不作任何保證。雖然不能保證硬實時工做方式,但Linux的實時調度算法的性能仍是很不錯的。2.6版的內核能夠知足嚴格的時間要求。
Linux調度程序提供強制的處理器綁定機制。雖然它盡力經過一種軟的(或者說天然的)親和性試圖使進程儘可能在同一個處理器上運行,但它也容許用戶強制指定「這個進程不管如何都必須在這些處理器上運行」。這種強制的親和性保存在進程的一個位掩碼標誌中。該掩碼標誌的每一位對應一個系統可用的處理器,默認狀況下全部的位都被設置。
Linux經過sched_ yield()系統調用,提供了一種讓進程顯式地將處理器時間讓給其餘等待執行進程的機制,它是經過將進程從活動隊列中(由於進程正在執行,因此它確定位於此隊列當中)移到過時隊列中實現的,由此產生的效果不只搶佔了該進程並將其放入優先級隊列的最後面,還將其放入過時隊列中—這樣能確保在一段時間內它都不會再被執行了,因爲實時進程不會過時,因此屬於例外,它們只被移動到其優先級隊列的最後面(不會放到過時隊列中)。
在Linux以的早期版本中,進程只會被放置到優先級隊列的末尾,放棄的時間每每不會太長,如今,應用程序甚至內核代碼在調用sched_ yield()前,應該仔細考慮是否真的但願放棄處理器時間。內核代碼爲了方便,能夠直接調用sched_ yield(),先要肯定給定進程確實處於可執行狀態,而後再調用sched_ yield(),用戶空間的應用程序直接使用sched_ yield()系統調用就能夠。
經過對本章進程調度的學習,我瞭解到進程調度程序是內核重要的組成部分,由於運行着的進程首先在使用計算機。可是知足進程調度的各類須要是較難實現的。例如公平調度中,越小的調度週期就會表現出越好的交互性,也更接近於「同時完成多任務」這一目標。然而系統必須承受更高的切換代價和更差的系統吞吐量,即魚與熊掌不可兼得。不過,Linux內核的新CFS調度程序儘可能知足了各個方面的需求,並以較完善的可伸縮性和新穎的方法提供了最佳的解決方案。