Linux核心學習筆記三 程序排程

2021-07-11 05:28:20 字數 3471 閱讀 1798

程序排程:

在可執行態程序之間分配有限處理器時間資源的核心子系統。

1 程序型別

i/o消耗型程序:大部分時間用來提交i/o請求或是等待i/o請求,經常處於可執行狀態,但執行時間短,等待請求過程時處於阻塞狀態。如互動式程式。

處理器消耗型程序:時間大都用在執行**上,除非被搶占否則一直不停的執行。

綜合型:既是i/o消耗型又是處理器消耗型。

排程策略要在:程序響應迅速(響應時間短)和最大系統利用率(高吞吐量)之間尋找平衡。

2 排程概念

優先順序:基於程序價值和對處理器時間需求進行程序分級的排程。

時間片:表明程序被搶占前所能持續執行的時間,規定乙個預設的時間片。時間片過長導致系統互動性的響應不好,

程式並行性效果差;時間片太短增大程序切換帶來的處理器耗時。矛盾!

時間片耗盡程序執行到期,暫時不可執行狀態。直到所有程序時間片都耗盡,重新計算程序時間片。

linux排程程式提高互動式程式優先順序,提供較長時間片;實現動態調整優先順序和時間片長度機制。

程序搶占:linux系統是搶占式,始終執行優先順序高的程序。

3 排程演算法

可執行佇列:runqueue;給定處理器上可執行程序的鍊錶,每個處理器乙個。每個可執行程序都唯一歸屬於乙個可執行佇列。

執行佇列是排程程式中最基本的資料結構:

struct

runqueue ;

提供了一組巨集來獲取給定cpu的程序執行佇列:

#define cpu_rq(cpu)         //

返回給定處理器可執行佇列的指標

#define this_rq() //

返回當預處理器的可執行佇列

#define task_rq(p) //

返回給定任務所在的佇列指標

在操作處理器任務佇列時候要用鎖:

__task_rq_lock

……__task_rq_unlock

4 schedule

系統要選定下乙個執行的程序通過呼叫schedule函式完成。

排程時機:

l  程序狀態轉換的時刻:程序終止、程序睡眠;

l  當前程序的時間片用完時(current->counter=0);

l  裝置驅動程式呼叫;

l  程序從中斷、異常及系統呼叫返回到使用者態時;

睡眠和喚醒:

休眠(被阻塞)的程序處於乙個特殊的不可執行狀態。休眠有兩種程序狀態:

task_interruptible:接收到訊號就被喚醒

task_uninterruptible:忽略訊號

兩種狀態程序位於同乙個等待佇列上,等待某些事件,不能夠執行。

程序休眠策略:

//

q是我們希望睡眠的等待佇列

declare_waitqueue(wait, current);

add_wait_queue(q, &wait);

//condition 是我們在等待的事件

while (!condition)

//程序被喚醒條件滿足 程序可執行狀態

set_current_state(task_running); //

將程序等待佇列中移除

remove_wait_queue(q, &wait);

程序通過執行下面幾個步驟將自己加入到乙個等待佇列中:

1) 呼叫declare_waitqueue()建立乙個等待佇列的項。

2) 呼叫add_wait_queue()把自己加入到佇列中。該佇列會在程序等待的條件滿足時喚醒它。

當然我們必須在其他地方撰寫相關**,在事件發生時,對等待佇列執行wake_up()操作。

3) 將程序的狀態變更為 task_interruptible或task_uninterruptible。

4) 如果狀態被置為task_interruptible,則訊號喚醒程序。這就是所謂的偽喚醒(喚醒不是因為事件的發生),因此檢查並處理訊號。

5) 檢查條件是否為真;如果是的話,就沒必要休眠了。如果條件不為真,呼叫schedule()。

6) 當程序被喚醒的時候,它會再次檢查條件是否為真。如果是,它就退出迴圈,如果不是,它再次呼叫schedule()並一直重複這步操作。

7) 當條件滿足後,程序將自己設定為task_running並呼叫remove_wait_queue()把自己移出等待佇列。

程序切換schedule函式呼叫context_switch()函式完成以下工作:

l  呼叫定義在中的switch_mm(),該函式負責把虛擬記憶體從上乙個程序對映切換到新程序中。

l  呼叫定義在中的switch_to(),該函式負責從上乙個程序的處理器狀態切換到新程序的處理器狀態。

這包括儲存、恢復棧資訊和暫存器資訊。

在前面看到schedule函式呼叫有很多種情況,完全依靠使用者來呼叫不能達到

很好的效果。核心需要判斷什麼時候呼叫schedule,核心提供了乙個need_resched標誌來表明是否需要重新執行一次排程:

l  當某個程序耗盡它的時間片時,scheduler_tick()就會設定這個標誌;

l  當乙個優先順序高的程序進入可執行狀態的時候,try_to_wake_up()也會設定這個標誌。

每個程序都包含乙個need_resched標誌,這是因為訪問程序描述符內的數值要比訪問乙個全域性變數快

(因為current巨集速度很快並且描述符通常都在快取記憶體中)。

1 使用者搶占

核心即將返回使用者空間時候,如果need_resched標誌被設定,會導致schedule函式被呼叫,此時發生使用者搶占。

使用者搶占在以下情況時產生:

l  從系統調返回使用者空間。

l  從中斷處理程式返回使用者空間。

2 核心搶占

只要重新排程是安全的,那麼核心就可以在任何時間搶占正在執行的任務。

什麼時候重新排程才是安全的呢?只要沒有持有鎖,核心就可以進行搶占。鎖是非搶占區域的標誌。由於核心是支援smp的,

所以,如果沒有持有鎖,那麼正在執行的**就是可重新匯入的,也就是可以搶占的。

為了支援核心搶占所作的第一處變動就是為每個程序的thread_info引入了preempt_count計數器。該計數器初始值為0,

每當使用鎖的時候數值加1,釋放鎖的時候數值減1。當數值為0的時候,核心就可執行搶占。從中斷返回核心空間的時候,

核心會檢查need_resched和preempt_count的值。如果need_resched被設定,並且preempt_count為0的話,這說明

有乙個更為重要的任務需要執行並且可以安全地搶占,此時,排程程式就會被呼叫。

核心搶占會發生在:

l  當從中斷處理程式正在執行,且返回核心空間之前。

l  當核心**再一次具有可搶占性的時候。

l  如果核心中的任務顯式的呼叫schedule()。

l  如果核心中的任務阻塞(這同樣也會導致呼叫schedule())。

Linux核心學習筆記三 程序排程

程序排程 在可執行態程序之間分配有限處理器時間資源的核心子系統。1程序型別 i o消耗型程序 大部分時間用來提交i o請求或是等待i o請求,經常處於可執行狀態,但執行時間短,等待請求過程時處於阻塞狀態。如互動式程式。處理器消耗型程序 時間大都用在執行 上,除非被搶占否則一直不停的執行。綜合型 既是...

筆記 Linux核心學習 三 之程序排程

程序排程 在可執行態程序之間分配有限處理器時間資源的核心子系統。1程序型別 i o消耗型程序 大部分時間用來提交i o請求或是等待i o請求,經常處於可執行狀態,但執行時間短,等待請求過程時處於阻塞狀態。如互動式程式。處理器消耗型程序 時間大都用在執行 上,除非被搶占否則一直不停的執行。綜合型 既是...

linux核心學習筆記 六 程序排程

linux 核心程序排程 linux 核心筆記 程序排程 linux 核心筆記 程序排程 4 程序排程示意圖 linux的程序排程如圖1所示。5 smp系統的排程 smp系統中的排程演算法的不同主要表現在排程演算法的最後,對於被切換出當前cpu執行權的程序呼叫了schedule tail函式,目的是...