關於Linux進程調度的詳解,Linux進程調度詳解

來源:互聯網
上載者:User

關於Linux進程調度的詳解,Linux進程調度詳解

4.1多任務

1、多任務作業系統就是能同時並發的互動執行多個進程的作業系統。

2、多任務作業系統使多個進程處於堵塞或者睡眠狀態,實際不被投入執行,這些任務儘管位於記憶體,但是並不處於可運行狀態。

3、多任務系統分類:

(1)非搶佔式多任務

(2)搶佔式多任務

4、Linux提供了搶佔式的多任務模式。在此模式下,由發送器來決定什麼時候停止一個進程的運行,以便其他進程能夠得到執行機會。這個強制的掛起動作叫做搶佔。進程被搶佔之前能夠啟動並執行時間是預先設定好的,叫進程的時間片。時間片實際上就是分配給每個可運行進程的處理器時間段。

5、在非搶佔式多任務模式下,除非進程自己主動停止運行,否則它會一直執行。進程主動掛起自己的操作稱為讓步。但這種機制有很多缺點:發送器無法躲每個進程該執行多長時間作出統一規定,所以進程獨佔的處理器時間可能會超過使用者的預料:更糟的是,一個絕不做出讓步的懸掛進程就能使系統崩潰。

4.2Linux的進程調度

1、O(1)調度器擁有數以十計的多處理器的環境,但缺少互動進程。

2、反轉樓梯期限調度演算法(RSDL),吸取了隊列理論,公平調度。又被稱為完美公平調度演算法(CFS)。

4.3策略

1、決定發送器在何時讓進程運行。

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

1、進程可以分為:

(1)I/O消耗型:進程的大部分時間用來提交I/O請求或者等待I/O請求,經常處於可運行狀態但是已耗用時間很短,等待更多的請求時最後總會阻塞。

(2)處理器消耗型:把時間大多用在執行代碼上,除非被搶佔,否則通常都會不停地運行。因為它們沒有太多的I/O需求。不屬於I/O驅動類型。

2、調度策略:盡量降低它們的調度頻率,延長其已耗用時間。

3、調度策略通常要在兩個矛盾的目標中間尋找平衡:

(1)進程調度迅速(回應時間短)

(2)最大系統利用率(高輸送量)

4、Linux傾向於優先調度I/O消耗型進程。

4.3.2進程優先順序

1、調度演算法中最基本的一類就是基於優先順序的調度,這是一種根據進程的價值和其對處理器時間的需求來對進程分級的想法。

2、發送器總是選擇時間片未用盡而且優先順序最高的進程運行。

3、Linux採用了兩種不同的優先順序範圍:

(1)nice

範圍[-20,19],預設值為0;nice值越大,優先順序越低;

Linux系統中nice值代表時間片的比例,可以通過ps-el命令查看系統中進程列表,結果中標記NI的一列及時進程對應的nice值。

(2)即時優先順序

其值可以配置,預設變化範圍是[0,99];值越高優先順序越高;

4、任何即時進程的優先順序都高於普通的進程,也就是說即時優先順序和nice優先順序處於互不相交的兩個範疇。

5、通過命令ps-eo state,uid,pid,ppid,rtprio,time,comm.查看系統中的進程列表以及對應的即時優先順序(位於RTPRIO列下),其中如果有進程對應列顯示“-”則說明它不是即時進程。

4.3.3時間片

1、時間片表示進程在被搶佔前所能持續啟動並執行時間。

2、I/O消耗型進程不需要很長的時間片,而處理器消耗型進程希望時間片越長越好。

3、Linux的CFS調度器沒有直接分配時間片到進程,而是將處理器的使用比劃分給進程。這樣一來,進程所獲得的處理器時間和系統負載密切相關。這個比例受nice值影響,nice值作為權重來調整進程所使用的處理器時間使用比。

4、Linux系統是搶佔式的,是否要將一個進程立刻投入運行(也就是搶佔當前進程),是完全由進程的優先順序和是否有時間片來決定。

5、CFS調度器:搶佔時機取決於新的可執行程式消耗了多少處理器使用比,如果消耗的使用比當前進程小:新程式立刻投入運行,搶佔當前進程,否則延遲。

4.3.4調度策略的活動

1、文字編輯程式顯然是 1/0 消耗型的,因為它大部分時間都在等待使用者的鍵盤輸入(無論使用者的輸入速度有多快,都不可能趕上處理的速度)使用者總是希望按下鍵系統就能馬上響應。

2、視頻編碼程式是處理器消耗型的。

3、CFS總是會毫不猶豫地讓文字編輯器在需要時被投入運行,而讓視頻處理常式只能在剩下的時刻運行。

4.4 Linux調度演算法

4.4.1調度器類

1、Linux調度器是以模組方式提供,目的是允許不同類型的進程可以有針對性地選擇調度演算法。這種模組化結構被稱為調度器類,它允許多種不同的可動態添加的調度演算法並存,調度屬於自己範疇的進程。

2、基礎的調度器代碼定義在kernel/sched.c檔案中。

3、每個調度器有一個優先順序,會按照優先順序順序遍曆調度類,選擇優先順序最高的調度器類。

4、完全公平調度CFS是一個針對普通進程的調度類。

4.4.2 Unix系統中的進程調度

1、Unix使用的調度演算法是分配絕對的時間片,這樣就會引發固定的切換頻率,不利於公平性。而Linux採用的CFS完全摒棄了時間片,分配給進程一個處理器使用比重,保證恒定的公平性和變動的切換頻率。

4.4.3公平調度

1、CFS的做怯是允許每個進程運行一段時間、迴圈輪轉、選擇運行最少的進程作為下一個運行進程,而不再採用分配給每個進程時間片的做法了,在所有可運行進程總數基礎上計算出一個進程應該運行多久。而不是依靠nice 值來計算時間片。

2、nice 值在 CFS 中被作為進程獲得的處理器運行比的權重:越高的nice 值(越低的優先順序)進程獲得更低的處理器使用權重。

3、目標延遲:無限小調度周期的近似值

4、最小粒度:每個進程獲得的時間片底線,預設為1ms。

5、任何進程所獲得的處理器時間是由它自己和其他所有可運行進程nice 值的相對差值決定的。

4.5 Linux調度的實現

1、CFS調度演算法的實現:

四個組成部分:

(1)時間記賬

(2)進程選擇

(3)調度器入口

(4)睡眠和喚醒

4.5.1時間記賬

1、所有的調度器都必須對進程已耗用時間做記賬。

2、CFS 使用調度器實體煩結構(定義在檔案的 struct_sched _entity 中)來追蹤進程運行記賬。

3、CFS 使用 vruntime 變數來記錄一個程式到底運行了多長時間以及它還應該再運行多久。

4、定義在kemeVsched_fair.c 檔案中的 update_curr()函數實現了該記賬功能。

6、update_ currO 計算了當前進程的執行時間,並且將其存放在變數delta_exec 中。update_ curr()是由系統定時器周期性調用。

4.5.2進程選擇

1、CFS調度演算法的核心:選擇具有最小vruntime的任務。

2、CFS使用紅/黑樹狀結構來組織可運行進程隊列,並利用其迅速找到最小vruntime值的進程。

3、Linux中,紅/黑樹狀結構被稱為rbtree,是一個自平衡二叉搜尋樹,是一種以樹節點形式儲存的資料,這些資料會對應一個索引值,可以通過這些索引值來快速檢索節點上的資料,(重要的是,通過索引值檢索到對應節點的速度與整個樹的節點規模成指數比關係)。

(1)挑選下一個任務

CFS的進程選擇演算法簡單總結為“運行rbtree樹種最左邊葉子節點所代表的那個進程”。實現這一過程的函數是__pick_next_entity()。

__pick_next_entity()函數本身不會遍曆樹找到最左葉子節點,該值緩衝在rb_leftmost欄位中,函數傳回值就是CFS選擇的下一個運行進程。如果返回NULL,表示樹空,沒有可運行進程,這時選擇idle任務運行。

(2)向樹中加入進程

發生在進程被喚醒或者通過fork()調用第一次建立進程時。

函數enqueue_entity():更新執行時間和其他一些統計資料,然後調用__enqueue_entity()。進行繁重的插入工作,把資料項目真正插入到紅/黑樹狀結構中:

link為null時迴圈終止,退出。

在父節點上調用rb_link_node(),使新插入的進程成為其子節點。

函數rb_insert_color()更新樹的自平衡相關特性。

(3)從樹中刪除進程

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

相關函數是dequeue_entity()和__dequeue_entity():

rb_erase()函數刪除進程

更新rb_leftmost緩衝

如果刪除的是最左節點,還要調用rb_next()按順序遍曆,找到新的最左節點。

4.5.3調度器入口

1、進程調度的主要進入點函數是schedule()。它定義在檔案kemel/sched.c中。

它會調用pick_next_task();pick_next_task()會以優先順序為序,從高到低依次檢查每一個調度類,並且從最高優先順序的調度類中選擇最高優先順序的進程。pick_next_task()會返回指向下一個可運行進程的指標,沒有時返回NULL。

2、pick_next_task()函數實現會調用pick_next_entity(),而該函數會調用__pick_next_entity()函數。

4.5.4睡眠和喚醒

1、休眠(被阻塞)的進程處於一個特殊的不可執行狀態。

2、進程休眠有很多原因,但肯定都是為了等待一些事件。

3、休眠的一個常見的原因就是檔案I/O——如進程對一個檔案執行了read()操作,而這需要從磁碟裡讀取。

4、核心的操作都相同:進程把自已標幟成休眠狀態,從可執行紅/黑樹狀結構中移出,放入等待序列,然後調用schedule()選擇和執行一個其他進程

5、喚醒的過程:進程被設定為可執行狀態,然後再從等待隊列中移到可執行紅/黑樹狀結構中。

6、休眠有兩種相關的進程狀態:

(1)TASK_INTERRUPTIBLE

(2)TASK_UNINTERRUPTIBLE

唯一區別是處於TASK_UNINTERRUPTIBLE的進程會忽略訊號,而處於TASK_INTERRUPTIBLE狀態的進程如果接收到一個訊號,會被提前喚醒並響應該訊號

7、等待隊列

等待隊列是由等待某些事件發生的進程組成的簡單鏈表

休眠通過等待隊列進行處理。

核心用wake_queue_head_t來表示等待隊列。

等待隊列可以通過DECLARE_WAITQUEUE()靜態建立,也可以由init_waitqueue_head()動態建立。

8、進程通過執行以下幾個步驟將自己加入到一個等待隊列中:

1)調用宏DEFINE_WAIT()建立一個等待隊列的選項。

2)調用add_wait_queue()把自己加入到隊列中。

3)調用prepare_to_wait()方法將進程的狀態變更為TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE。

4)如果狀態被設定成TASK_INTERRUPTIBLE,則訊號喚醒進程。

5)當進程被喚醒的時候,會再次檢查條件是否為真,真則退出迴圈,否則再次調用schedule()並且一直重複這步動作。

6)當條件滿足後,進程將自己設定為TASK_RUNNING並調用finish_wait()方法把自己移出等待序列。

函數inotify_read():負責從通知檔案描述符中讀取資訊。

9、喚醒操作通過函數wake_up()進行,它會喚醒指定的等待隊列上的所有進程。它調用try_to_wake_up(),該函數負責將進程設定成TASK_RUNNING狀態,調用enqueue_task()將此進程放入紅/黑樹狀結構中,如果被喚醒的進程優先順序比正在執行的進程優先順序高,設定need_resched標誌。通常哪段代碼促成等待條件達成,它就負責隨後調用wake_up()函數。

10、關於休眠有一點需要注意,存在虛假的喚醒。有時候進程被喚醒並不是因為它所等待的條件達成了才需要用一個迴圈處理來保證它等待的條件真正達成。

4.6搶佔和環境切換

1、環境切換,也就是從一個可執行進程切換到另一個可執行進程,由定義在 kernel/ sched.c 中的 context_switch()函數負責處理。

2、每當一個新的進程被選出來準備投入啟動並執行時候, schedule()就會調用該函數。它完成了兩項基本的工作:

(1)調用聲明在 中的 switch_mm(),該函數負責把虛擬記憶體從上一個進程映射到新進程中。

(2)調用聲明在 中的 switch_to(),該函數負責從上一個進程的處理器狀態切換到新進程的處理器狀態。這包括儲存、恢複棧資訊和寄存器資訊,還有其他任何與體繫結構相關的狀態資訊,都必須以每個進程為對象進行管理和儲存。

3、核心提供了一個need_resched標誌來表明是否需要重新執行一次調度。當某個進程應該被搶佔時,scheduler_tick()會設定這個標誌:當一個優先順序高的進程進入可執行狀態時,try_to_wake_up()會設定這個標誌。核心檢查這個標誌確認其被設定,調用schedule()來切換到一個新的進程。該標誌對於核心來說是一個資訊,表示youqitajinc應當被運行了,要儘快調用發送器。再返回使用者空間以及從中斷返回時,核心也會檢查標誌。每個進程都包含一個need_resched標誌,因為訪問進程描述符裡的數值比訪問一個全域變數要快。

4.6.1使用者搶佔

1、核心即將返回使用者空間的時候,如果need_resched標誌被設定,會導致schedule()被調用,此時會發生使用者搶佔。

2、使用者搶佔在以下情況時產生:

(1)從系統調返回使用者空間時;

(2)從中斷處理常式返回使用者空間時;

4.6.2核心搶佔

1、Linux完整地支援核心搶佔。

2、只要重新調度是安全的,核心就可以在任何時間搶佔正在執行的任務。

3、核心搶佔會發生在:

(1)中斷處理常式正在執行,且返回核心空間之前

(2)核心代碼再一次具有可搶佔性的時候。

(3)如果核心中的任務顯式地調用 schedule()

(4)如果核心中的任務阻塞(這同樣也會導致調用schedule())。

4.7即時調度策略

1、Linux提供了兩種即時調度策略:SCHED_FIFO和 SCHED_RR。而普通的、非即時的調度策略是SCHED_NORMAL。

2、SCHED_FIFO 實現了一種簡單的、先入先出的調度演算法。

3、SCHED_RR 是帶有時鬧片的 SCHED_FIFO,一種即時輪流調度演算法。

4、這兩種即時演算法實現的都是靜態優先順序。核心不為即時進程計算動態優先順序,這能保證給定優先順序別的即時進程總能搶佔優先順序比它低的進程。

5、軟即時:核心調度進程,儘力使進程在它的限定時間到來前進行,但核心不保證總能滿足這些進程的要求。

6、硬即時:系統保證在一定條件下,可以滿足任何調度的要求。

7、優先順序範圍

(1)即時:

範圍:0~[MAX_RT_PRIO-1]。

預設MAX_RT_PRIO=100,所以預設即時優先順序範圍為[0,99]

(2)SCHED_NORMAL:

範圍: [MAX_RT_PRIO]~[MAX_RT_PRIO+40]

預設情況下,nice值從-20到+19對應的是從100到139的即時優先順序範圍。

4.8與調度相關的系統調用

4.8.1與調度策略和優先順序相關的系統調用

1、sched_setparam()和sched__getparam()分別用於設定和擷取進程的即時優先順序

2、nice()函數可以將給定進程的靜態優先順序增加一個給定的量,只有超級使用者才能在調用它時使用負值,從而提高進程的優先順序。

3、nice()函數會調用核心的 set_ user_ nice()函數,這個函數會設定進程的 task_struct 的 static_prio 和prio 值。

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

1、Linux發送器提供強制的處理器綁定機制。也就是說,雖然它儘力通過一種軟的親和性試圖使進程盡量在同一個處理器上運行,但它也允許使用者強制指定“這個進程無論如何都必須在這些處理器上運行”。 這種強制的親和性儲存在進程task_struct的cpus_allowed這個位元遮罩標誌中。

2、進程只運行在指定處理器上,對處理器的指定是由該進程描述符的 cpus_allowed 網域設定的。

4.8.3放棄處理器時間

1、Linux通過 sched_yieldO 系統調用,提供了一種讓進程顯式地將處理器時間讓給其他等待執行進程的機制。

2、核心代碼為了方便,可以直接調用yield(),先要確定給定進程確實處於可執行狀態,然後再調用 sched__yield()。

3、使用者空間的應用程式直接使用 sched__yield()系統調用就可以了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.