帶你玩轉(zhuǎn)linux內(nèi)核源碼分析之CFS調(diào)度
一、CFS 完全公平調(diào)度
允許每個進程運行一段時間、循環(huán)輪轉(zhuǎn),選擇運行最少的進程作為下一個運行進程。
依靠所有可運行總數(shù)基礎(chǔ)上計算出一個進程應(yīng)該運行多久。
nice優(yōu)先級用來計算權(quán)重
內(nèi)核源碼 kernel/sched/fair.c
二、調(diào)度實現(xiàn)
1、時間記賬
每個進程只能在公平分配給它的處理器時間運行
時間在調(diào)度實體struct sched_entity中維護
虛擬實時 vruntime,該運行時間計算是經(jīng)過了所有可運行進程總數(shù)的標(biāo)準(zhǔn)化。以ns為單位。用vruntime記錄了一個程序到底運行了多長時間以及還應(yīng)該運行多久。
elta_exec:當(dāng)前進程的執(zhí)行時間,參數(shù)有又遞給calc_delta_fair。
calc_delta_fair 再根據(jù)當(dāng)前可運行進程總數(shù)對運行時間進行加權(quán)計算,將最終權(quán)重值與當(dāng)前進程的vruntimes相加。curr->vruntime += calc_delta_fair(delta_exec, curr);
update_curr是由系統(tǒng)定時器周期性調(diào)用的,無論進程在可運行還是阻塞狀態(tài),vruntime可以準(zhǔn)確地計算出給定進程的運行時間,而且制定誰應(yīng)該是下一個被運行進程
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。?!前100名進群領(lǐng)取,額外贈送一份價值699的內(nèi)核資料包(含視頻教程、電子書、實戰(zhàn)項目及代碼)? ?


2、調(diào)度器入口
schedule() 選擇哪個進程可以運行,何時將其投入運行。
調(diào)用pick_next_task,會以優(yōu)先級排序,從高到低,依次檢查每個調(diào)度類,并從最高優(yōu)先級調(diào)度類中,選擇最高優(yōu)先級的進程
schedule->__schedule->pick_next_task->pick_next_task_fair schedule->__schedule->pick_next_task->context_switch
上下文切換本身通過調(diào)用兩個特定于處理器的函數(shù)完成
switch_mm 更換通過task_struct->mm描述的內(nèi)存管理上下文
switch_to切換處理器寄存器內(nèi)容和內(nèi)核棧,通常使用匯編編寫
switch_to(prev, next, prev);//之后的代碼只有在當(dāng)前進程的下一次被選擇運行時才會執(zhí)行
? ?barrier();//前后語句執(zhí)行順序不會因為任何可能的優(yōu)化而改變
? ?return finish_task_switch(prev);//完成清理工作,才能夠正確釋放鎖
3、進程選擇

選擇vruntime最小也就是紅黑樹中最左側(cè)葉子節(jié)點 實現(xiàn)函數(shù)pick_next_entity->__pick_next_entity
4、睡眠和喚醒
睡眠時進程會標(biāo)記為休眠狀態(tài),從可執(zhí)行紅黑樹中移除,放入等待隊列,然后調(diào)用schedule選擇執(zhí)行下一個進程。
喚醒:進程被設(shè)置為可執(zhí)行狀態(tài),從等待隊列中移到可執(zhí)行紅黑樹中
check_preempt_wakeup->wakeup_preempt_entity
三、CFS調(diào)度中vruntime
新創(chuàng)建的vruntime是多少?
阻塞的進程在調(diào)度時,runtime是多少?
周期調(diào)度,vruntime時間如何計算?
如果進程比較多,進程所獲得時間片時間趨于0,系統(tǒng)有何策略,調(diào)度優(yōu)化的參數(shù)有哪些?
1.新進程vruntime的值
do_fork --> copy_process --> sched_fork --> task_fork_fair
減掉min_vruntime的數(shù)值,是因為你這個時候的新進程不跟CPU關(guān)聯(lián)起來,實際在運行的時,可能會掛到其它CPU的CFS隊列,如果兩個進程CPU處理器的min_vruntime差距太大,會導(dǎo)致新進程優(yōu)先級很低或者很高,主要是為了解決這個問題,在入新CPU處理順路時,會再加上min_vruntime。新進程vruntime=父進程vruntime + (cpux_min_vruntime - cpuy_min_vruntime)。
設(shè)置好時間后 接著執(zhí)行 判斷是否搶占wake_up_new_task->activate_task->check_preempt_wakeup->wakeup_preempt_entity
wakeup_preempt_entity在上面有介紹
2.阻塞的進程在調(diào)度時,runtime是多少?
activate_task–>enqueue_task_fair–>enqueue_entity
由于內(nèi)核已經(jīng)承諾,在當(dāng)前的延遲周期內(nèi)所有使用活動進程都至少運行一次,隊列的min_vruntime用作基準(zhǔn)虛擬時間,通過減去sysctl_sched_latency的一半,則可以確保新喚醒的進程只有在當(dāng)前延遲周期結(jié)束后才能運行。
如果睡眠進程已經(jīng)積累了比較大的不公平。則內(nèi)核必須考慮。如果se->vruntime比計算的差值更大,則將其作為進程的vruntime,這會導(dǎo)致該進程在紅黑樹中比較靠左的位置。
3.周期調(diào)度,vruntime時間如何計算
對于單 CPU 而言, 默認(rèn)設(shè)置是小于 8 個就緒任務(wù)時就按照 6ms, 大于 8 個就緒任務(wù)時, 每個任務(wù)給 0.75 ms.

確保沒有哪個進程能夠比延遲周期中確定的份額(給定的時間)運行時間更長
ideal_runtime:該份額對應(yīng)的時間(給定的時間) delta_exec:進程在CPU上已經(jīng)運行的時間
1)已經(jīng)運行的時間與份額比較
2)已經(jīng)運行的時間與下一個調(diào)度實體時間比較
4.調(diào)度器優(yōu)化變量
sysctl_sched_child_runs_first:child在fork之后調(diào)度的策略,如果為0 則先調(diào)用parent sched_min_granularity_ns:最低級別搶占粒度。給每個進程分配CPU時間,如果進程無限多,則時間趨向于0。如果小于sched_min_granularity_ns則以sched_min_granularity_ns為準(zhǔn) sysctl_sched_min_granularity:如果執(zhí)行時間delta_exec < sysctl_sched_min_granularity則不進行調(diào)度。unsigned int sysctl_sched_min_granularity = 750000ULL
