Linux程式排程核心實現分析

FreeeLinux發表於2017-01-08

本文根據兩篇部落格總結,它們的地址分別是:
http://blog.csdn.net/giantpoplar/article/details/51791002
http://blog.csdn.net/giantpoplar/article/details/51791002

目錄

所謂搶佔

  • 搶佔式
    • 排程程式決定程式何時停止,為其他程式騰出 CPU 資源,這種強制掛起行為就是搶佔。程式被搶佔前執行時間為提前設定好的時間片。
  • 非搶佔
    • 程式佔據 CPU 資源不放,除非自己主動呼叫 schedule() 函式,這稱為程式讓步。

什麼是排程

排程的原因:現代作業系統都是多工的,為了能讓作業系統更好的發揮多工的特性,需要一個管理程式來管理所有任務,這個程式就是排程程式。

排程程式的功能有:

  1. 負責讓那些程式執行,那些程式等待
  2. 決定每個程式執行多長時間

此外為了更好地使用者體驗,執行中的程式還可以立即被其他更緊急的程式打斷。

總之排程是一個平衡的過程。一方面,它要保證各個執行的的程式能夠最大限度使用CPU(即儘量少的切換程式),另一方面,保證各個程式公平使用CPU(即防止一個程式長時間獨佔CPU的情況)

排程實現原理

排程主要基於程式優先順序和時間片實現。

程式的優先順序

程式的優先順序有兩種度量方法,一種是nice值,一種是實時優先順序
nice 值的範圍是 -20~+19,值越大優先順序越低,也就是說 nice 值為-20的程式優先順序最大。
實時優先順序的範圍是 0~99,與 nice 值的定義相反,實時優先順序是值越大優先順序越高。
實時程式都是一些對響應時間要求比較高的程式,因此係統中有實時優先順序高的程式處於佇列的話,它們會搶佔一般程式的執行時間。

程式的實時優先順序高於 nice 值,在核心中,實時優先順序的範圍是 0~MAX_RT_PRIO-1。
MAX_RT_PRIO的定義參見 include/linux/sched.h

    #define MAX_USER_RT_PRIO    100
    #define MAX_RT_PRIO         MAX_USER_RT_PRIO

nice值在核心中的範圍是 MAX_RT_PRIO~MAX_RT_PRIO+40 即 MAX_RT_PRIO~MAX_PRIO

    #define MAX_PRIO            (MAX_RT_PRIO + 40)

一個程式有隻能擁有實時優先順序和 nice 值其中之一

時間片

有了優先順序,就可以決定誰先執行了。但是對於排程程式來說,並不是執行一次就結束了,必須知道間隔多久進行下次排程。
於是就有了時間片的概念。時間片表示一個程式搶佔前能持續執行的時間。
時間片設定,過大會讓系統響應變慢;過小會產生由於程式頻繁切換帶來的開銷。
預設的時間片一般為 10ms。

排程的實現原理

基本步驟我的理解是:

  1. 首先確定每個程式佔用多少 CPU 時間,如利用 nice 值進行計算。
  2. 讓佔用 CPU 時間多的先執行。(讓列寧同志先走)
  3. 執行完後,扣除執行程式的 CPU 時間,接下來繼續重複這三步。

Linux上排程實現的方法

Linux 上採用了“完全公平排程演算法”,簡稱“CFS”。
CFS 演算法在分配給每個程式 CPU 時間時,不是分配給它們一個 CPU 的絕對時間,而是根據程式的優先順序分配給它們一個佔用 CPU 時間的百分比。

舉例:

程式名 nice值 百分比
程式A 1 10%
程式B 3 30%
程式C 6 60%

由上圖可知,佔據百分比之和就是 100%。所以我的理解是 Linux 下 CFS 演算法的主要步驟有:

  1. 計算每個程式的 vruntime,通過 update_curr() 函式更新程式的 vruntime。
  2. 選擇具有最小 vruntime 的程式投入執行。
  3. 程式執行完後,更新 vruntime,再回到步驟 2。

vruntime 變數存放程式的虛擬執行時間,該執行時間(花在執行上的時間和)是通過對可執行總數的加權後得出的,單位是 ns。計算權重實際上就是計算佔用 CPU 的百分比,通過它得出 vruntime,用來記錄一個程式執行了多長時間以及還應執行多久。

  • 時間記賬
    使用 sched_entity 結構體進行記賬,該結構體在 task_struct 中被包含。vruntime 作用上面說過了。
  • 程式選擇
    使用紅黑樹存放程式佇列,以 vruntime 為鍵值,每次選擇 vruntime 最小的程式。這裡有用到了一個 trick,使用 leftmost 快取了紅黑樹的最左葉子節點,提高效率(STL也這麼幹了)。
  • 排程器入口
    schedule() 函式呼叫 pick_next_task() 函式,按照優先順序從大到小一次檢查每一個排程類,從最高優先順序排程類中,選擇最高優先順序程式。
  • 睡眠和喚醒
    睡眠:程式標記為 TASK_INTERRUPTIBLE 或 TASK_UNINTERRUPTIBLE 狀態,從紅黑樹中移除,放入等待佇列,呼叫 schedule 選擇和執行一個其他的程式
    喚醒:通過 wake_up() 函式喚醒,程式標記為 TASK_RUNNING 狀態,從等待佇列放入紅黑樹中。

上下文切換

  • switch_mm() 函式把虛擬記憶體從上一個程式切換到新程式。
  • switch_to() 從上一個處理器狀態切換到新處理器狀態,儲存回覆棧暫存器資訊。

呼叫schedule()的時機

  • 某個程式應該被搶佔,scheduer_tick 設定為 need_resched。
  • 一個優先順序高的程式進入可執行狀態,try_wake_up 設定為 need_resched
  • 從核心返回使用者空間或從中斷返回,核心檢查 need_resched。如果已經設定,核心會在繼續執行之前呼叫排程程式

搶佔時機

使用者搶佔:

  1. 從系統呼叫返回使用者空間。
  2. 從中斷處理程式返回使用者空間。

核心搶佔:

  1. 中斷處理程式正在執行,且返回核心空間之前。
  2. 核心程式碼再一次具備可搶佔性的時候(我認為就是出於臨界區之外時)。
  3. 如果核心中的程式碼顯示呼叫 schedule()。
  4. 如果核心中任務阻塞。

實時排程

SCHED_FIFO:不使用時間片,先入先出。只有更高優先順序別的SCHED_FIFO,SHED_RR 程式才能搶佔,同一級別不能搶佔。
SCHED_RR:帶有時間片,實時輪流排程。時間片耗盡,允許同一級別程式搶佔。

相關文章