LKD(Linux内核设计与实现) Chapter 4 Process Scheduling
一个理想化的多任务CPU能够同时执行所有任务,每个任务依据优先级权重分得应得的计算资源。但这样的CPU是不存在的,只能通过某种手段模拟。为此,CFS调度器设计了一个参数叫Virtual Runtime(简称vruntime)来记录当前任务所获得的运行时间。背后的道理是,相同优先级的任务所获得vruntime应该相同,某个任务获得少了,说明该任务受到不公平对待,应给其更多调度机会。
数据结构
CFS相关的数据结构主要定义在kernel/sched/sched.h
。
单个任务的的时间统计是放在struct task_struct
/struct sched_entity
下:
所有可以运行的任务以vruntime为键值按小到大排序构成一个timeline。Timeline使用一个红黑树来组织,树最左边的节点为vruntime最小的任务。红黑树的根节点以及最左节点都放在CFS的runqueue结构体struct cfs_rq
中:
虚拟时钟
vruntime的更新是通过update_curr
函数(定义在kernel/sched/fair.c
)。update_curr
计算当前任务获得的执行时间,累计其vruntime。此外,update_curr
还做了一个动作:执行更新min_vruntime。
min_vruntime定义在struct cfs_rq
中,这是个只增不减的值,主要作用是为新进任务的vruntime提供一个参照。如果直接把新进任务的vruntime设为0,会与现有的vruntime产生较大差距,导致调度器分配过多资源给新进任务。合适的做法是给新进任务设置一个足够小的vruntime,让其能够较快被调度。但初始调度之后,就能进入常态,和其他任务一样根据公平性来调度。所以min_vruntime是用来正常化(normalize)vruntime的。
Nice权重
vruntime之所以和任务获得的真实执行时间不一样,是因为它是以Nice值为权重加权计算出来的值。累计vruntime的时候需除以权重再累加。Nice值高的任务,权重低,vruntime就累计得快;nice值低的任务,权重高,vruntime累计得慢。所以Nice值低的任务就能获得更多真实执行时间。
update_curr
调用calc_delta_fair
函数对vruntime的累加进行加权。权重序列以几何级数方式散开,从而保证nice值的比较优先级,即两个任务获得CPU比例只和相应nice值的差别有关系,和nice值的绝对值没有关系。
-
文章中涉及的任何代码都参考Linux v4.4。
-
Documentation/scheduler/sched-design-CFS.txt