程序排程(二)

2021-07-03 09:08:15 字數 3595 閱讀 3218

緊接上一文!!!!

​3:程序選擇

在cfs排程裡面,當需要選擇下乙個程序的時候,將會選擇最小的vruntime的程序。這個其實就是cfs排程的演算法的核心。

cfs使用紅黑樹來組織可執行程序佇列,並利用其迅速找到最小的vruntime值的程序。在linux中,紅黑樹是乙個子平衡的二叉搜尋樹。

下面我們就來看一下如何挑選下乙個vruntime最小的程序。

根據紅黑樹的原理,假設vruntime就是節點的鍵值,那麼如果要尋找最小的vruntime的程序,就是從樹的根節點開始,一直向左尋找,一直找到樹的最左邊的節點即為vruntime最小的節點。

static

struct sched_entity *__pick_next_entity(struct cfs_rq *cfs_rq)

看了這段**,很明顯,並沒有進行尋找,而是直接獲取了執行佇列中的rb_leftmost成員,其實,這就是linux中的乙個用空間省時間的乙個做法。他先將最小vruntime的節點儲存下來,儲存成rb_leftmost成員,當使用的時候,直接拿來使用就可以,但是當每次vruntime更新的時候,都會重現最一下選擇,選擇出最小的vruntime的節點儲存到該成員中。如果找不到最小的vruntime的程序,那麼cfs排程其將會選擇idle任務執行。

2):向樹中加入程序

當程序變為可執行狀態或者是通過fork()呼叫第一次建立程序的時候,cfs就需要將程序加入到rbtree中。現在我們來看一下如何將程序加入到rbtree中,在函式enqueue_entity中被實現。

static

void

enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)

update_stats_enqueue(cfs_rq, se);

check_spread(cfs_rq, se);

if (se != cfs_rq->curr)

__enqueue_entity(cfs_rq, se);

}

該函式更新可執行時間和其他的一些統計資料,然後,呼叫函式__enqueue_entity()函式向rbtree中插入節點,將資料真正插入到rbtree中。

下面我們看一下函式__enqueue_entity()。

/*

* enqueue an entity into the rb-tree:

* 向rbtree中新增乙個實體

*/static void __enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se)

else }/*

* maintain a cache of leftmost tree entries (it is frequently

* used):

** 維護乙個快取,存放最左葉子節點(他會經常被使用)

*/if (leftmost)

cfs_rq->rb_leftmost =

&se->run_node;

rb_link_node(&se->run_node, parent, link);

rb_insert_color(&se->run_node, &cfs_rq->tasks_timeline);

}

如果對這些**不太理解的話,也不用太擔心,當我們後面學習了紅黑樹的時候,這些問題就都理解了。這裡對節點的插入就不詳細講解,後面會專門講解紅黑樹。

3):從樹中刪除程序

當程序堵塞或者是終止的時候,cfs需要將程序從rbtree中刪除,下面我們來看一下cfs是如何刪除程序的。

static void

dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int sleep)

#endif

}//清楚占有的記憶體

clear_buddies(cfs_rq, se);

if (se != cfs_rq->curr)

__dequeue_entity(cfs_rq, se);

account_entity_dequeue(cfs_rq, se);

update_min_vruntime(cfs_rq);

/** normalize the entity after updating the min_vruntime because the

* update can refer to the->curr item and we need to reflect this

* movement in our normalized position.

** 當更新min_vruntime之後,規範化排程器實體,因為更新可以指向"->curr"

* 項,我們需要在規範化的位置來反映這個變化

*/if (!sleep)

se->vruntime -= cfs_rq->min_vruntime;

}

根據**,實際上是呼叫__dequeue_entity()函式來刪除rbtree中的實體。

下面我們看一下**:

static void __dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se)

rb_erase(&se->run_node, &cfs_rq->tasks_timeline);

}

具體的對於rbtree的操作我們後面具體詳解。

從紅黑樹中刪除節點比較容易,因為rbtree實現了rb_erase()函式,可以完成所有工作。該函式剩下的工作就是更新rb_leftmost快取,如果刪除的是最左節點,則需要遍歷樹,找到下乙個最左節點。

4:排程器入口

排程器的主要入口是函式schedule(),定義在檔案kernel/sched.h檔案中。他是核心其他部分用於呼叫程序排程器的入口:選擇哪個程序可以執行,何時將其投入執行。該函式唯一重要的事情就是呼叫pick_next_task()函式,該函式會以優先順序為序,從高到低,依次檢查每乙個排程器類,並且從最高優先順序排程器類中選擇最高優先順序的程序。

下面我們看一下這個函式:

/*

* pick up the highest-prio task:

* 選擇最高優先順序程序

*/static

inline

struct task_struct *

pick_next_task(struct rq *rq)

class = sched_class_highest;

for ( ; ; )

}

該函式開始部分的優化比較有意思。因為cfs是普通程序的排程類,而系統的絕大部分程序是普通程序,所以這裡使用該技巧來加速一下下乙個cfs提供的程序。但是前提是所有的可執行程序都是cfs類的。

該函式的主要部分是for迴圈,從最高的優先順序類開始,遍歷了每乙個排程類。每乙個排程類都實現了pick_next_task()函式,他會返回下乙個可執行程序或者是沒有時返回null,我們會從第乙個返回非null的類中選擇下乙個可執行程序。

OS之程序排程(二)

時間片輪轉演算法 在分時系統中,最簡單最常用的是基於時間片的輪轉排程演算法,讓就緒佇列上的每個程序僅執行乙個時間片。輪 的基本原理 在就緒佇列中,系統可設定每隔一定時間便產生乙個中斷,去啟用程序排程程式進行排程,把cpu 分配給隊首程序,並令其執行乙個時間片,當其執行完畢後,又把處理機分配給佇列的新...

程序排程二 程序建立do fork

一 前言 kernel在啟動初期並沒有 程序 這個概念,因為不涉及多工併發 排程,kernel 起來後會在start kernel 中建立kthread和init程序,在0號程序的基礎上建立init程序 pid為1 0 程序會被設定成idle程序,加入到執行 佇列中。當cpu上沒有可排程程序時,排程...

程序排程演算法(程序排程策略)

程序排程演算法 排程演算法是指 根據系統的資源分配策略所規定的資源分配演算法。一 先來先服務和短作業 程序 優先排程演算法 1.先來先服務排程演算法。先來先服務 fcfs 排程演算法是一種最簡單的排程演算法,該演算法既可用於作業排程,也可用於程序排程。fcfs演算法比較有利於長作業 程序 而不利於短...