说一下CFS

linux的进程调度器由三个部件组成:主调度器,调度器类,上下文切换。主调度器利用
调度器类对象获取一下个应该被执行的进程,然后进行底层的上下文切换。
内核中的每个调度器类都必须提供一个调度器实例,没有动态添加调度器实例的方法,这
些实例按照重要性链接起来,例如实时调度器在链头,CF调度器紧跟在后。我想是如果系
统存在实时进程则进程调度在实时调度器中就会结束。


进程有不同的优先级,根据这个优先级,每个进程在一轮调度(每个就绪进程都执行一遍

)中运行一定的时间。明显高优先级的运行时间应该长一些,而低优先级则短一些。在
CFS中,理想的运行时间计算公式为:
ideal_time = sum_runtime * se.weight/cfs_rq.weight
sum_runtime就是一轮调度的总时间:
(1) sum_runtime=sysctl_sched_min_granularity * nr_running(if 进程数 > 5)
(2) sum_runtime=sysctl_sched_latency = 20 ms           (if 进程数 <= 5)
注:sysctl_sched_min_granularity =4ms
weight是由优先级nice值转变而来的,至于为什么会有这样的转变,以及为什么会是这样
的转变,that's a question.

以上确定了进程在一轮调度中应该占据的时间,现在要说怎样从运行队列中选取下一个
应该被执行的进程。
总概念:完全公平调度器(CFS)的公平体现在各个进程向前推进的速度是相等的。
CFS basically models an "ideal, precise multi-tasking CPU" on real hardware.
CFS在真实硬件上模拟一个理想的,精确的多任务CPU,"ideal muti-tasking CPU"可以
想象为能以(1)相同速度(2)同时间运行nr_running(当前运行队列中的进程总数)个
进程的CPU,这个CPU对各个进程来说无疑是公平的。
但是,这种CPU并不真实存在,所以才有'models'一说。在真实的CPU上(1)相同速度可以
满足,问题出在(2)同时运行,即并行。CFS利用虚拟运行时间(vruntime)以串行模拟
并行。理想CPU同时运行的结果就是各个进程会运行同样的时间(设进程同时开始,且
不会结束),所以只要保证进程运行同样的时间,即vruntime(为什么不是真实时间下面
会说)一样即成功模拟了同时运行,另一个角度来看保证vruntime的增长速度一样即可。


举个例子,设运行队列中有A B C三个进程,优先级都一样,则每个进程在一轮调度中应
占据1/3的运行时间。假设先运行A,当A结束后它的vruntime就增加1/3的运行时间,然后
被放回队列中。这个时候由于BC的vruntime比A少,则认为是受到了不公平对待,所以应
选择B或C,而不能再选择A。也就是说,这个队列应该以各个进程的vruntime为键进行升
序排序,每次选择队首的进程。


上面的例子直接把进程真实的运行时间加在vruntime上,这是因为各进程的优先级一样。
然而在真实环境下毕竟会不不同优先级的进程,高优先级的进程运行长一点的时间是公平
的,如果直接把真实的运行时间加于vruntime,则高优先级的进程的vruntime会比低优先
级进程增长更快,这将导致不公。
CFS采用的vruntime计算公式保证了公平,每次时钟中断,当前运行的进程的vruntime会
如此更新:
vruntime += delta * NICE_0_LOAD/se.weight
其中delta是从最近一次时钟中断到当前时刻进程运行的时间,注意这个值并不一定等于
时钟中断间隔,毕竟有其它中断能抢占当前进程。
可以看到,在相同的delta下,优先级越大(则se.weight越大)的进程vruntime增加得
越少。
举个例子,设一个高优先级的进程A一轮调度消耗x个时钟中断,而一个低优先级的进程
B消耗y个时钟中断,明显x*delta > y*delta,但x*delta/x.weight与y*delta/y.weight
的关系就不一定了。实际上,每一轮每个进程的vruntime(增量)为:
vruntime = ideal_time * NICE_0_LOAD/se.weight
= sum_runtime * NICE_0_LOAD/cfs_rq.weight
所有进程都一样。也就是说在这种计算公式下,vruntime的增长速度对各个进程来说都
一样,这就成功用串行模拟了并行,保证了公平。
当然这是运行队列稳定(没有增加或减少进程)的情况。正是由于运行队列是不稳定的,
ideal_time每时每刻都可能变化,所以在每次时钟中断都要更新vruntime,否则在只在
进程被调出的时候更新就可以了。然而就算队列是不稳定的,不稳定因素对每个进程
也是平均的,所以vruntime依然可以作为运行队列的键。


CFS采用rb-tree代替普通的队列,键值也不是vruntime,是min_vruntime-vruntime,但

意义也差不多。

调度流程大致为:

tick中断

              update_curr(cfs_rq) /* 更新进程的实际及虚拟运行时间 */

       check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr) /* 判断进程的实际运行时间是否已经达到ideal_time,是则进行调度 */

其它新增进程,进程阻塞的情况有时间再写吧。

           

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值