緊接上一文!!!!
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演算法比較有利於長作業 程序 而不利於短...