linux內核--進程調度

      調度程是內核的組成部分,它負責選擇下一個要運行的進程。進程調度程序可看作在可運行態進程之間分配有限的處理器時間的內核子系統。Linux是一個多任務操作系統,只有通過調度程序的合理調度,系統資源才能最大限制的發揮作用,多進程纔會有併發執行的效果。

       多任務操作系統分爲非搶佔式多任務和搶佔式多任務。linux是一個搶佔式的多任務操作系統,由調度程序決定什麼時候停止一個進程的運行以便其它進程能夠得到執行機會,這個強制的掛起動作就叫做搶佔。進程在被搶佔之前能夠運行的時間是預先設置好的,叫進程的時間片。時間片實際是分配給每個可運行的進程的處理器時間段。而在非搶佔式多任務模式下,除非進程自己主動停止,否則它會一直執運行。

策略

      策略決定調度程序在何時讓什麼進程運行。進程可以分爲I/O消耗型和處理器消耗型。前者指進程的大部分時間用來提交I/O請求或等待I/O請求。因此,這樣的進程經常處理可運行狀態,但通常都是運行短短一會兒,因爲它在等待更多的I/O請求時最後總會阻塞。相反,處理消耗型進程把時間大多用在執行代碼上。除非被搶佔,否則它們通常一直不停地運行,因爲它們沒有太多的I/O需求。對於這類處理消耗型的進程,調度策略是儘量降低它們的運行頻率,對於它們而言,延長其運行時間會更合適些。

      調度策略通常在兩個矛盾的目標中間尋找平衡:進程響應迅速(響應時間短)和最大系統利用率(高吞吐量)。調度算法中最基本的一類就是基於優先級的調度。優先級高的先運行,低的後運行,相同優先級的進程按輪轉方式進去調 度(一個接一個,重複進行)。在包括Linux在內的某些操作系統中,優先級高的進程使用的時間片也較長。調度程序總是選擇時間片未用盡且優先級高的進程運行。用戶和系統都可以通過設置進程的優先級來影響系統的調度。

      Linux根據以上思想實現了一種基於動態優先級的調度方法。一開始,該方法先設置基本的優先級,然而它允許調度程序根據需要來加、減優先級。舉個例子,如果一個進程在I/O等待上耗費的時候多於其運行時間,那麼該進程顯屬於I/O消耗型進程。它的優先級會被動態提高。相反,如果一個進程的全部時間片一下就被耗盡,那麼該進程屬於處理器消耗型進程,它的優先級會被動態降低。

      時間片是一個數值,它表時進程在被搶佔前所能持續運行的時間。調 度策略必須規定一個默認的時間片,時間片過長會導致系統對交互的響應表現欠佳;讓人覺得系統無法併發執行應用程序;時間片過短會明顯增大進程切換帶來的消耗,因爲肯定會有相當一部分系統時間用在進程切換上,而這些進程能夠用來運行的時間片卻很短。當一個進程的時間片耗盡時,就認爲這個進程到期了。

       linux系統是搶佔式的,當一個進程進入task_running狀態,內核會檢查它的優先級是否高於當前正在執行的進程。如果是這樣,調度程序會被喚醒,搶佔當前正在運行的進程並運行新的可運行進程。此外,當一個進程的時間片變爲0時,它會被搶佔,調度程序被響醒以選擇一個新的進程。

調度程序的可執行隊列

      調度程序中最基本的數據結構是運行隊列,可執行隊列是給處理器上的可執行進程的鏈表,每個處理器一個。每個可投入執行的進程都惟一歸屬於一個可執行隊列。每個運行隊列都有兩個優先級數組,一個活躍的和一個過期的。優先級數組使可運行處理器的每一種優先級都包含一個相應的隊列,而這些隊列包含對應優先級上的可執行進程鏈表。

       許多操作系統在所有進程的時間片用完時,都採用一種顯式的方法來重新計算每個進程的時間片。當一個進程的時間片耗盡時,它會被移至過期數組,但在此之前,時間片已經給它重新計算好了。

       選定一個進程並切換到它去執行是通過schedule()函數來實現的。當內核想要休眠時,會直接調用函數,另外,如果有哪個進程被搶佔,那麼該函數也會被喚起執行。schedule()函數獨立於每一個處理器運行。因此,每個處理器對下一次該運行哪個進程做出自己的判斷。

       調度程序通過進程的休眠時間來判斷一個進程到底是I/O消耗型還是處理消耗型。爲了支持這種推斷機制,linux記錄了一個進程用於休眠和用於執行的時間,這個值存在於task_struct的sleep_avg域中。

       休眠(被阻塞)的進程處理一個特殊的不可執行狀態。進程休眠有各種原因,但肯定是爲了等待一些事件。事件可能是一段時間、從文件I/O讀更多數據,或者是某個硬件事件。休眠的一個常見原因就是文件I/O--如進程對一個文件執行了read()操作,而這需要從磁盤裏讀取。還有,進程在獲取鍵盤輸入的時候也需要等待。無論哪種情況,內核的操作都相同:進程把它自己標記爲休眠狀態,把自己從可執行隊列移出,放入等待隊列,然後調用schedule()選擇和執行一個其它進程。喚醒的過程剛好相反:進程被設置爲可執行狀態,然後再從等待隊列中移到可執行隊列。

休眠和喚醒狀態圖

休眠和喚醒切換圖

 

負載平衡程序

linux的調度程序爲對稱多下得系統的每個處理器準備了單獨的可執行隊列和鎖,也就是說,每個處理器擁有一個自己的進程鏈表,而它只對屬於自己的這些進程進行調度操作。出地效率考慮,整個調度系統從每個處理器來看都是獨立的。如果出現一個處理器的隊列上有5個進程,而另外一個處理器的隊列上只有1個進程,該怎麼辦呢?這些問題由負載平衡程序解決,它負責保證可執行隊列之間的負載平衡狀態。如果發現了不均衡,就會把相對繁忙的隊列中的進程抽到當前的可執行隊列中來。理想狀態下,每個隊列上的進程數目應該相等。在單處理器系統中,不會執行負載均衡程序。

搶佔和上下文切換

      上下文切換,也就是從一個可執行進程切換到另一個可執行進程。每當一個新的進程被選出來準備投入運行的時候,schedule()就會調用上下文切換功能函數context_switch()。

實時

linux提供了兩種實時調度策略:SCHED_FIFO和SCHED_RR。而普通的、非實時的調度策略是SCHED_NORMAL。SCHED_FIFO實現了一種簡單的、先入先出的調度算法,它不使用時間片。SCHED_FIFO級的進程比任何SCHED_NORMAL級的進程都先得到調度。一旦一個SCHED_FIFO級進程處於可執行狀態,應該會一直執行,直到它自己受阻塞或顯示的釋放處理器爲止,它不基於時間片,可以一直執行下去。只有較高優先級的SCHED_FIFO或SCHED_RR任務才能搶佔SCHED_FIFO任務。如果有兩個或者更多的SCHED_FIFO進程,它們會輪流執行。SCHED_RR與SCHED_FIFO大體相同,只是SCHED_RR級的進程在耗盡事先分配給它的時間後就不能再接着執行了。這兩種實時算法實現的都是靜態優先級。

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