进程的优先级
首先我们关注Linux的进程优先级的设定,Linux有两种优先级:
- nice值
- 实时优先级
nice值的范围是[-20,19],数字越大则优先级越低,我们可以使用ps -el
查看包含nice值的进程列表
列表中的NI一列就表示进程的nice值。
实时优先级的范围是[0,99],与nice值的意义相反,数字越大表示优先级越高。
时间片
很多操作系统直接分配一个时间片给进程,进程调度运行完给定的时间片就会让出CPU。而CFS调度器不一样的地方在于,它给定每个进程不是一个时间片,而是处理器的使用比例。如果某个就绪的进程的CPU使用比例小于正在CPU上运行的进程的处理器使用比,那这个进程就会抢占CPU立即投入运行。而进程优先级越高,能得到的处理器使用比例就越大。优先级越低,处理器的使用比例就越小。
CFS使用vruntime变量来表示这个处理器使用比例。初始的时候根据nice值给每个进程的vruntime一个初始值,nice值越大、优先级越低的进程vruntime就越大。注意vruntime越大表示该进程已经得到的处理器比例就越大,因此为了保证公平,应该调度vruntime越小即当前获得处理器比例较小的进程运行,以使得这些所有进程获得公平的处理器使用比例。
CFS调度实现
时间记账
由于CFS没有时间片的概念,取而代之的是处理器使用比例的概念,因此进程运行时就要记录自己的运行时间。CFS使用vruntime变量来实现这个功能,但是并不是简单保存运行时间,而是根据当前可运行进程总数对运行时间进行一个加权计算,这个加权值含义可以理解为进程这次运行得到的处理器使用比例,最终将这个加权值加上vruntime得到新的vruntime。
为了实现尽量公平,更新vruntime是系统定时器周期性更新的,而不是进程运行结束之后。因为CFS没有时间片的概念,进程不会有运行完给定时间片被抢占的情况,而是周期性更新自己的vruntime,当发现有一个vruntime更小的就绪进程时,这个进程就会被更小vruntime的进程抢占CPU。
进程选择
前面提到,为了保证公平,CFS会挑一个具有最小vruntime的进程运行。CFS实现这个功能使用红黑树,红黑树的每个节点的键值就表示可运行进程的vruntime,因此为了获得vruntime最小的进程,只需要找到红黑树中最左边的一个叶子节点即可。
CFS为了找到最左边的叶子节点,不是每次去查找这颗红黑树,而是维护一个rb_leftmost的变量,每次取出这个变量的值即可。虽然在红黑树中查找一个节点复杂度为O(nlgn),并不是一个很高的复杂度,但是维护这个变量效率更高。因为每次更新这颗红黑树时,如果插入的节点或删除的节点并不是最左边的叶子节点,那这个变量不需要改变。而如果是,那这个变量的改变代价也很低,因为已经访问到最左边的叶子节点了,只需要做一次赋值即可。