Linux CFS中的进程调度

时间:2022-02-16 03:52:41

使用优先级映射时间片的劣势

在Linux中用nice值代表优先级,它的范围是-20~19。nice值越低,优先级越高。

在Linux之前,Unix中就是使用nice值映射时间片的方式来进行调度。比如,nice值为0对应100ms的时间片,nice值为20对应5ms的时间片,nice值为19对应10ms的时间片。

在一般情况下,如果是nice值为0和nice值为20的两个进程同时运行,那么他们对应的时间片分别为100ms和5ms。这种情况中,nice值为0的进程拥有20/21的处理器时间,让优先级更高的拥有更多的处理器的处理时间是非常合理的。

但是在另一些情况下可能表现就并非这么理想了。
如果是两个同等的低优先级的进程,比如他们都是nice值为20的进程,那么他们的时间片都为5ms,于是他们每运行5ms就会进行一次进程切换。如此就放大的进程切换所带来的消耗,但是我们最初希望的是两个进程都占有CPU 50%的时间。在这样的情况下,显然无法实现。

CFS理想状态

CFS全称完全公平调度算法。
在理想情况下,每个进程将能获得1/n的处理器时间——n是指可运行进程的数量。

但是有很多因素使理想状态无法实现:
1、一个处理器无法同时运行多个进程,进程切换本身有消耗,还会影响到缓存的效率。
2、当时间片小到一定的程度,进程抢占的代价会被放大。

CFS实现

在CFS中不再是nice值与时间片的绝对映射,而是让nice值对应不同的权重,然后根据权重和实际运行时间来分配时间片。
比如有A、B两个进程,他们对应的weight分别为1和3,周期时间为20ms,那么A需要运行的时间就是1/4*20=5ms,B需要运行的时间就是3/4*20=15ms。

nice与weight的对应对如下:

static const int 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,
};

在这种情况下,运行的时间都是按照权值来计算,而不是之前的绝对时间,这样就不会出现“时间片过小,放大进程切换所带来的消耗”的问题了。

CFS下的进程选择

主要思想:
CFS根据vruntime的值,将所有的进程存入红黑树,每次在红黑树中选择vruntime最小的进程运行。同时CFS为了实现公平,必须惩罚正在运行的进程,以使那些正在等待的进程下次会被调度。(否则就会一直调用vruntime最小的进程到运行结束为止)

vruntime是虚拟运行时间,计算公式如下:(1024代表nice=0时的权重,上面表中有)
vruntime = 实际运行时间 * 1024 / 进程权重 (公式1)

由公式所得,如果一个进程运行的事件越长,那么它的vruntime的值也会越大,被调用的概率也会越小。
因此,优先级越高,已经运行时间越短的进程最会被调用。

另外这个公式还有一个演化:
vruntime = (调度周期 * 进程权重 / 所有进程总权重) * 1024 / 进程权重 = 调度周期 * 1024 / 所有进程总权重 (公式2)

从这个公式我们可以看到,虽然进程的权重不同,但是它们的 vruntime增长速度应该是一样的 ,与权重无关。因此,通过vruntime 来选择运行的进程,既能公平选择进程,又能保证高优先级进程获得较多的运行时间。这就是CFS的主要思想了。