註:這裡並沒有詳細分析到每個內建函式,如果要瞭解這些細節的話,可以看後面的OTHER CFS CLASS API及CFS主要的內建函式。
周期性調度器在調度架構上由scheduler_tick完成:在每一個cpu的刻度都觸發一次該函數調用,它更新執行隊列的時鐘及load,然後調用當前進程的調度器類的周期調度函數。
update_rq_clock(rq); /* 更新執行隊列的時鐘rq->clock*/ update_cpu_load_active(rq); /* 更新執行隊列load,本質是將數組中先前儲存的負荷值向後移動一個位置,將當前負荷記入數組的第一個位置 */ curr->sched_class->task_tick(rq, curr, 0);
我們再來看一下CFS調度器類的周期調度函數(task_tick_fair),該函數是對entity_tick的組調度的一個封裝(因為只有真正的task的se才會在cpu上運行,而group的se是不會在cpu上啟動並執行,所以這裡參數的se就是一個task,然後對從它到它所在的組的根的所有group
se調用entity_tick),所以我們直接來看一下entity_tick函數:
update_curr(cfs_rq); //完成當前se(這個se可以是task或group)的執行時間,虛擬時間,cfs_rq的相應時間及統計的更新:curr->exec_start=now;curr->sum_exec_runtime+=delta_exec;及curr->vruntime+= calc_delta_fair();更新cfs_rq的min_vruntime.其中delta_exec表示實質執行的時間,而calc_delta_fair則計算delta_exec相對應的虛擬時間,即delta_fair={if se->load.weight != NICE_0_LOAD return delta_exec; else return delta_exec *NICE_0_LOAD/se.load->weight},可以看到當該se是0優先順序的話,那麼它的虛擬時間等於實質執行的物理時間,否則如果該se的load越大那麼它的虛擬時間就越小,這也是為什麼load越大的進程能夠執行的時間越多——它的虛擬時間增長的越慢update_entity_shares_tick(cfs_rq); //這個函數只有對於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;#endifif (cfs_rq->nr_running > 1 || !sched_feat(WAKEUP_PREEMPT)) //可啟動並執行大於1,或者不支援WAKEUP_PREEMPT,後面這個條件在後來的版本已經被去掉了check_preempt_tick(cfs_rq, curr); //該函數用於檢查當前進程是否運行了足夠長的時間(實質已耗用時間大於sched_slice理想已耗用時間或者當前運行隊列最左邊的se到現在的等待的時間已經超過curr理想應該啟動並執行時間),如果出現了上面的情況則調用resched_task將該進程設定為TIF_NEED_RESCHED,即可調度的;如果已耗用時間小於sysctl_sched_min_granularity(最小執行時間)或者核心不允許WAKEUP_PREEMPT(核心允許的調度特性在/proc/sys/kernel/sched_features定義WAKEUP_PREEMPT使用第3 bit),則直接返回。注意這裡的每個判斷條件的順序是非常重要的(前面的更重要):1.如果進程已經運行超過它的理想已耗用時間那麼它將無條件被resched_task;2.如果不支援WAKEUP_PREEMPT那麼直接返回不對當前的進行resched_task;3.如果啟動並執行時間小於sysctl_sched_min_granularity那麼也直接返回;4.最左邊的等待時間大於理想已耗用時間時resched_task(curr),即要4滿足的話,必須當前的執行時間小於它理想已耗用時間,並且支援WAKEUP_PREEMPT及運行了至少sysctl_sched_min_granularity,最後已經等待的時間大於理想已耗用時間
可以看到每個時鐘都對會當前啟動並執行se進行實質執行時間及虛擬執行時間進行更新,最後檢查該進程是否啟動並執行足夠長的時間,如果是的話則將它置為TIF_NEED_RESCHED供主調度在適當的時機進行切換。可見周期性調度器還是比較簡單,沒有涉及到真正的調度任務,只是設定一個重調度請求的標誌而已,下面看看主調器,也就是它來響應周期性調度器的TIF_NEED_RESCHED。
圖 CFS與周期調度器