注:這里并沒有詳細(xì)分析到每個內(nèi)部函數(shù),如果要了解這些細(xì)節(jié)的話,可以看后面的
OTHER CFS CLASS API及
CFS主要的內(nèi)部函數(shù)。
周期性調(diào)度器在調(diào)度框架上由
scheduler_tick完成:在每一個cpu的時鐘周期都觸發(fā)一次該函數(shù)調(diào)用,它更新運行隊列的時鐘及l(fā)oad,然后調(diào)用當(dāng)前進程的調(diào)度器類的周期調(diào)度函數(shù)。
- update_rq_clock(rq); /* 更新運行隊列的時鐘rq->clock*/
- update_cpu_load_active(rq); /* 更新運行隊列l(wèi)oad,本質(zhì)是將數(shù)組中先前存儲的負(fù)荷值向后移動一個位置,將當(dāng)前負(fù)荷記入數(shù)組的第一個位置 */
- curr->sched_class->task_tick(rq, curr, 0);
我們再來看一下CFS調(diào)度器類的周期調(diào)度函數(shù)(
task_tick_fair),該函數(shù)是對
entity_tick的組調(diào)度的一個封裝(因為只有真正的task的se才會在cpu上運行,而group的se是不會在cpu上運行的,所以這里參數(shù)的se就是一個task,然后對從它到它所在的組的根的所有g(shù)roup se調(diào)用entity_tick),所以我們直接來看一下
entity_tick函數(shù):
- update_curr(cfs_rq); //完成當(dāng)前se(這個se可以是task或group)的執(zhí)行時間,虛擬時間,cfs_rq的相應(yīng)時間及統(tǒng)計的更新:curr->exec_start=now;curr->sum_exec_runtime+=delta_exec;及curr->vruntime+= calc_delta_fair();更新cfs_rq的min_vruntime.其中delta_exec表示實質(zhì)執(zhí)行的時間,而calc_delta_fair則計算delta_exec相對應(yīng)的虛擬時間,即delta_fair={if se->load.weight != NICE_0_LOAD return delta_exec; else return delta_exec *NICE_0_LOAD/se.load->weight},可以看到當(dāng)該se是0優(yōu)先級的話,那么它的虛擬時間等于實質(zhì)執(zhí)行的物理時間,否則如果該se的load越大那么它的虛擬時間就越小,這也是為什么load越大的進程能夠執(zhí)行的時間越多——它的虛擬時間增長的越慢
-
- update_entity_shares_tick(cfs_rq); //這個函數(shù)只有對于SMP才有作用,所以我們這里先不分析
-
- #ifdef CONFIG_SCHED_HRTICK //HRTICK情況,不知道干嘛
- if (queued) {
- resched_task(rq_of(cfs_rq)->curr);
- return;
- }
- if (!sched_feat(DOUBLE_TICK) &&
- hrtimer_active(&rq_of(cfs_rq)->hrtick_timer))
- return;
- #endif
-
- if (cfs_rq->nr_running > 1 || !sched_feat(WAKEUP_PREEMPT)) //可運行的大于1,或者不支持WAKEUP_PREEMPT,后面這個條件在后來的版本已經(jīng)被去掉了
- check_preempt_tick(cfs_rq, curr); //該函數(shù)用于檢查當(dāng)前進程是否運行了足夠長的時間(實質(zhì)運行時間大于sched_slice理想運行時間或者當(dāng)前運行隊列最左邊的se到現(xiàn)在的等待的時間已經(jīng)超過curr理想應(yīng)該運行的時間),如果出現(xiàn)了上面的情況則調(diào)用resched_task將該進程設(shè)置為TIF_NEED_RESCHED,即可調(diào)度的;如果運行時間小于sysctl_sched_min_granularity(最小執(zhí)行時間)或者內(nèi)核不允許WAKEUP_PREEMPT(內(nèi)核允許的調(diào)度特性在/proc/sys/kernel/sched_features定義WAKEUP_PREEMPT使用第3 bit),則直接返回。注意這里的每個判斷條件的順序是非常重要的(前面的更重要):1.如果進程已經(jīng)運行超過它的理想運行時間那么它將無條件被resched_task;2.如果不支持WAKEUP_PREEMPT那么直接返回不對當(dāng)前的進行resched_task;3.如果運行的時間小于sysctl_sched_min_granularity那么也直接返回;4.最左邊的等待時間大于理想運行時間時resched_task(curr),即要4滿足的話,必須當(dāng)前的執(zhí)行時間小于它理想運行時間,并且支持WAKEUP_PREEMPT及運行了至少sysctl_sched_min_granularity,最后已經(jīng)等待的時間大于理想運行時間
可以看到每個時鐘都對會當(dāng)前運行的se進行實質(zhì)執(zhí)行時間及虛擬執(zhí)行時間進行更新,最后檢查該進程是否運行的足夠長的時間,如果是的話則將它置為TIF_NEED_RESCHED供主調(diào)度在適當(dāng)?shù)臅r機進行切換??梢娭芷谛哉{(diào)度器還是比較簡單,沒有涉及到真正的調(diào)度任務(wù),只是設(shè)置一個重調(diào)度請求的標(biāo)志而已,下面看看主調(diào)器,也就是它來響應(yīng)周期性調(diào)度器的
TIF_NEED_RESCHED。
圖 CFS與周期調(diào)度器