下面通过一个举例来说明CFS调度器虚拟运行时间vruntime和时间片slice是如何计算的。本文涉及的公式,读者可以自行参考如下文件:
include/uapi/linux/sched.h kernel/sched/fair.c
CFS调度器中task时间片slice以及vruntime的计算公式:
period:调度周期,如果就绪队列上任务个数小于,等于8时,默认为6ms,大于8时则为0.75ms*任务个数;
slice = period * weight/total_weight ;根据任务的权重占就绪队列总权重的比例来瓜分调度周期。
vruntime = delta_exec*nice_0_weight/weight ;CFS调度器总是选择vruntime值最小的任务进行调度执行。
linux为了计算便利,将上面公式的除法转换为乘法和移位操作,将中间的过程值提前计算好,通过查表法来获取中间值,具体细节本文不再赘述,读者可以查看其他资料,本文重点是计算过程。
下面通过理论上的计算加深对CFS调度器的理解:
假设某核上现在15个任务,对应的nice值如下:
任务id | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
nice值 | 0 | 0 | 0 | 0 | -20 | 1 | 5 | 0 | 0 | -20 | -20 | -20 | 0 | 19 | -10 |
weight值 | 1024 | 1024 | 1024 | 1024 | 88761 | 820 | 335 | 1024 | 1024 | 88761 | 88761 | 88761 | 1024 | 14 | 9548 |
调度周期 period:0.75ms*15=11.25ms
总权重 total_weigth:1024*7+88761*4+820+335+15+9548=372930
各个任务对应的调度时间片(单位ms):
任务 1,2,3,4,8,9 slice = 11.25*1024 /372930=0.030890515
任务 5,10,11,12 slice = 11.25*88761/372930=2.67761
任务 6 slice = 11.25*820 /372930=0.02474
任务 7 slice = 11.25*335 /372030=0.01011
任务 14 slice = 11.25*15 /372930=0.00045
任务 15 slice = 11.25*9548 /327930=0.28803
各个任务对应的vruntime值:
任务 1,2,3,4,8,9 vruntime=0.030890515*1024/1024 =0.030890515
任务 5,10,11,12 vruntime=2.67761 *1024/88761 =0.03089051092
任务 6 vruntime=0.02474 *1024/820 =0.03089482926
任务 7 vruntime=0.01011 *1024/335 =0.0309034
任务 14 vruntime=0.00045 *1024/15 =0.03072
任务 15 vruntime=0.28803 *1024/9548 =0.03089052
各个任务的nice值对应的权重weight值差值很大,且每个任务瓜分到的时间片也存在很大的差异,但是每个任务的虚拟运行时间vruntime值却非常的接近。
CFS调度器是根据各个任务vruntime值选择vruntime值最小的任务进行调度执行。
各个任务的vruntime值是一个累加值,每次执行后需要累加本次调度计算出来的vruntime值。每次时钟中断到达时会对vruntime以及负载等统计数据进行更新,同时判断该任务的时间片slice即上面计算的值是否用完,如果时间片用完则设置该任务thread_info->flags |= TIF_NEED_RESCHED标志,TIF_NEED_RESCHED标志表明该任务需要被调度,时钟中断返回前会检查任务flag是否置位TIF_NEED_RESCHED,如果置位则进行任务调度,否则该任务继续执行,使用CPU资源。
上面计算出来的结果只是一个理论值,因为在实际的CPU环境上各个任务由于这样或者那样的原因造成任务的休眠,阻塞时间片还未使用完被迫进行调度让出CPU资源,
其实际vruntime值和理论vruntime值存在差异。预测系统的调度行为以及任务实际使用的时间片存在难点。