前言
將一個處於睡眠狀態或者新建立的程序加入就緒佇列時會產生喚醒搶佔檢查,被喚醒的任務一般期望能夠立刻執行,發生搶佔能夠滿足被喚醒任務的實時性需求。CFS排程器的喚醒搶佔能否成功會受到sysctl_sched_wakeup_granularity
的影響,該引數能控制喚醒搶佔發生的機率。頻繁的搶佔有可能會造成大的上下文切換開銷,影響業務吞吐,因此sysctl_sched_wakeup_granularity
是排程效能最佳化的一個重要引數。
喚醒搶佔的觸發條件
CFS排程類中的每一個任務都有一個虛擬時間vruntime
,虛擬時間小的程序會優先被排程,被喚醒任務se
搶佔當前任務curr
的只需要滿足虛擬時間差vdiff
超過喚醒搶佔粒度gran
,顯然可知粒度越小發生搶佔的機率越大。
// fair.c
static int
wakeup_preempt_entity(struct sched_entity *curr, struct sched_entity *se)
{
int ret;
s64 gran, vdiff = curr->vruntime - se->vruntime;
...
gran = wakeup_gran(se);
if (vdiff > gran) // 超過喚醒搶佔的粒度,搶佔成功
return 1;
return 0;
}
搶佔粒度的影響因素
喚醒搶佔粒度的計算結果受兩方面影響:
sysctl_sched_wakeup_granularity
:該引數作為基底直接影響喚醒粒度的大小,可以改變系統整體的喚醒搶佔發生機率- 被喚醒任務的權重:搶佔喚醒還需要考慮到任務之間的優先順序差異,排程器會優先保障高優先順序任務的執行權,換句話說如果被喚醒任務的優先順序高於正在執行的任務那麼搶佔就會更容易發生,反之搶佔會更容易失敗。
因此calc_delta_fair()
將sysctl_sched_wakeup_granularity
按照被喚醒任務se
的權重進行了加工,se
的權重越大計算得到的喚醒搶佔粒度越小。
// fair.c
static unsigned long wakeup_gran(struct sched_entity *se)
{
unsigned long gran = sysctl_sched_wakeup_granularity;
...
return calc_delta_fair(gran, se);
}
調整sysctl_sched_wakeup_granularity
預設情況下sysctl_sched_wakeup_granularity
為1 msec * (1 + ilog(ncpus))
,比如4核機器預設值為3ms。
Linux v5.13
之前的版本可以透過echo X > /proc/sys/kernel/sched_wakeup_granularity
或者sudo sysctl kernel.sched_wakeup_granularity_ns=X
進行修改。
Linux v5.13
以後引數被移動到了/sys/kernel/debug/sched/*
目錄下。