CFS原理

CFS定义了一种新的模型,它给运行队列中的每个进程都设置了一个虚拟时钟,即vruntime。如果一个进程被调度器投入运行,随着时间的增长,其vruntime将不断增大,而没有得到执行的进程vruntime则不会发生变化。调度器总是选择vruntime最小的那个进程来运行,这就是所谓的“完全公平”。为了区分不同优先级的进程,优先级高的进程vruntime增长相对较慢,因此可以得到更多的运行机会。

CFS基本设计思路

CFS思路很简单,就是根据各个进程的权重分配运行时间。

进程的运行时间计算公式为:分配给进程的运行时间 = 调度周期 * 进程权重 / 所有进程权重之和

调度周期很好理解,就是将所有处于TASK_RUNNING态的进程都调度一遍的时间。举个例子,假如系统中只有两个进程A, B处于TASK_RUNNING态,权重分别为1和2,调度周期为30ms,则分配给A的CPU时间为 30ms * (1/(1+2)) = 10ms;而分配给B的CPU时间为 30ms * (2/(1+2)) = 20ms。也就是说,在这30ms中A将运行10ms,B将运行20ms。

公平怎么体现呢?它们的运行时间并不一样阿?其实公平是体现在另外一个量上面,叫做virtual runtime(vruntime),它记录着进程已经运行的时间,但是并不是直接记录,而是要根据进程的权重将运行时间放大或者缩小一个比例。我们来看下从实际运行时间到vruntime的换算公式:vruntime = 实际运行时间 * 1024 / 进程权重

为了不把大家搞晕,这里我直接写的1024,实际上它就是nice优先级为0的进程的权重,对应源码中为NICE_0_LOAD。也就是说,所有进程都以nice优先级为0的进程的权重作为基准,计算自己的vruntime增长速度。还以上面A、B两个进程为例,B的权重是A的2倍,那么B的vruntime增长速度只有A的一半。现在我们把 (公式2) 中的实际运行时间用 (公式1) 替换,就可以得到这么一个结果:
vruntime = (调度周期 * 进程权重 / 所有进程总权重) * 1024 / 进程权重 = 调度周期 * 1024 / 所有进程总权重

看出什么眉目没有?没错,虽然进程的权重不同,但是它们的 vruntime增长速度应该是一样的 ,与权重无关。好,既然所有进程的vruntime增长速度宏观上看应该是同时推进的,那么就可以用这个vruntime来选择运行的进程了。调度器认为,谁的vruntime值较小,就说明它以前占用CPU的时间较短,受到了“不公平”对待,因此下一个被运行的进程就是它。这样既能公平地选择进程,又能保证高优先级的进程获得更多的运行时间。这就是CFS的主要思想了。

或者可以这么理解:CFS的思想就是让每个进程的vruntime互相追赶,而由于每个进程的vruntime增加速度不同,权重越大的增加的越慢,这样就能获得更多的CPU执行时间了。