本文对CFS调度算法关键知识点进行梳理
nice 值和运行时间的关系
nice 值的范围-20 ~ 19,进程默认的nice值为0。这些值类似与级别,可以理解成40个等级,nice 值越高,优先级越低,nice值越低,优先级越高。
为什么这么设定,因为nice表示进程友好程度,值越大,对其他进程越友好,就会让出cpu时间给其他进程。
进程每降低一个nice级别,优先级提高一个等级,响应进程可多获得10%的cpu时间。
进程每提升一个nice级别,优先级则降低一个级别,响应进程少获得10%的cpu时间。
nice值相当于系数1.25。
内核提供的nice值与权重对应关系表
代码路径: kernel/sched/core.c
/*
* Nice levels are multiplicative, with a gentle 10% change for every
* nice level changed. I.e. when a CPU-bound task goes from nice 0 to
* nice 1, it will get ~10% less CPU time than another CPU-bound task
* that remained on nice 0.
*
* The "10% effect" is relative and cumulative: from _any_ nice level,
* if you go up 1 level, it's -10% CPU usage, if you go down 1 level
* it's +10% CPU usage. (to achieve that we use a multiplier of 1.25.
* If a task goes up by ~10% and another task goes down by ~10% then
* the relative distance between them is ~25%.)
*/
const int sched_prio_to_weight[40] = {
/* -20 */ 88761, 71755, 56483, 46273, 36291,
/* -15 */ 29154, 23254, 18705, 14949, 11916,
/* -10 */ 9548, 7620, 6100, 4904, 3906,
/* -5 */ 3121, 2501, 1991, 1586, 1277,
/* 0 */ 1024, 820, 655, 526, 423,
/* 5 */ 335, 272, 215, 172, 137,
/* 10 */ 110, 87, 70, 56, 45,
/* 15 */ 36, 29, 23, 18, 15,
};
举例:
假设进程A和B的nice值都为0,那么理想运行时间计算公式
1024 / (1024 + 1024) = 50%
1024是因为A和B的进程权重值1024
当A 进程nice值增加到1,B依然是0
对于A进程理想运行时间
820 / (820 + 1024) = 44.5%
对于B进程理想运行时间
1024 / (820 + 1024) = 55.5%
vruntime 值怎么来的
vruntime = ( delta_exec * nice_0_weight ) / weight
vruntime 表示进程虚拟的运行时间,delta_exec 表示实际运行时间,nice_0_weight 表示nice值为0的进程的权重值,weight表示该进程的权重值。
sched_period 怎么算的
sched_period 是下面说的调度周期,也就是本次调度周期总的cpu时间,是物理运行时间,通过函数 __sched_period 计算得到
代码路径: kernel/sched/fair.c
/*
* The idea is to set a period in which each task runs once.
*
* When there are too many tasks (sched_nr_latency) we have to stretch
* this period because otherwise the slices get too small.
*
* p = (nr <= nl) ? l : l*nr/nl
*/
static u64 __sched_period(unsigned long nr_running)
{
if (unlikely(nr_running > sched_nr_latency))
return nr_running * sysctl_sched_min_granularity;
else
return sysctl_sched_latency;
}
当进程的数目小于8时,则调度周期等于调度延迟等于6ms。当系统的进程数目大于8时,则调度器周期等于进程的数目乘以0.75 ms,系统要保证每个进程最小调度时间0.75 ms。
ideal_runtime 值怎么来的
ideal_runtime 表示理想运行时间,也就是分配给进程的时间
分配给进程的真实运行时间 = 本次调度周期总的cpu时间 * ( 当前进程的权重 / 就绪队列(runnable)所有进程权重之和 )
所以得出结论
ideal_runtime = 本次调度周期总的cpu时间 * ( 当前进程的权重 / 就绪队列(runnable)所有进程权重之和 )
根据 sched_period 可知,总时间固定的情况,每个进程理想运行时间也是知道的,如果一个进程是cpu密集型,每次调度会使用整个理想时间,如果是IO密集型的,应该用不完这个时间。
总结
sched_period 的目的是决定每个task在本次调度周期可以运行的最大时间。
如果某个task超过了自身的 ideal_runtime,则会自动让出调度的机会,CFS选择 vruntime 最小的task运行,并在下次得到调度的时候,重置 sched_period 和 ideal_runtime。
vruntime 决定的是哪个进程先调度,sched_period 决定的是每个进程在当前调度周期的最大执行时间。