linux内核调度在2.6.23 之前使用的大名鼎鼎的O(1)算法。O(1) 调度器跟踪运行队列中可运行的任务(实际上,每个优先级水平有两个运行队列 — 一个用于活动任务,一个用于过期任务), 这意味着要确定接下来执行的任务,调度器只需按优先级将下一个任务从特定活动的运行队列中取出即可)。 O(1) 调度器扩展性更好而且包含交互性,提供了大量启示用于确定任务是受 I/O 限制还是受处理器限制。 相比cfs调度, O(1) 调度器在内核中很笨拙,且复杂。在代码里要转很久才能转出来。
cfs 最核心的概念是虚拟时间,cfs中没有真正意义上的调度时间,而是用虚拟时间去代替。虚拟时间--打个比方就是一个变速器 ,对于权重大的任务虚拟时间跑得慢,也就是他出现在红黑树(快速高效地插入或删除任务)的最左侧的机会更大,他也就会更有机会被调度到。而权重小的就相反。用更官方的语言:CFS 不直接使用优先级而是将其用作允许任务执行的时间的衰减系数。 低优先级任务具有更高的衰减系数,而高优先级任务具有较低的衰减系数。 这意味着与高优先级任务相比,低优先级任务允许任务执行的时间消耗得更快。 这是一个绝妙的解决方案,可以避免维护按优先级调度的运行队列。
这就是精髓所在吧。当然,虚拟时间不只是参考权重,既然叫cfs,就应该体现公平。具体操作:任务存储在以时间为顺序的红黑树中(由 sched_entity 对象表示),对处理器需求最多的任务 (最低虚拟运行时)存储在树的左侧,处理器需求最少的任务(最高虚拟运行时)存储在树的右侧。 为了公平,调度器然后选取红黑树最左端的节点调度为下一个以便保持公平性。任务通过将其运行时间添加到虚拟运行时, 说明其占用 CPU 的时间,然后如果可运行,再插回到树中。这样,树左侧的任务就被给予时间运行了,树的内容从右侧迁移到左侧以保持公平。 因此,每个可运行的任务都会追赶其他任务以维持整个可运行任务集合的执行平衡。
任务 调度器 cfs 红黑树 的层次结构关系如下
更详细的红黑树结构如下图,如果想详细了解红黑树可以参看我前面的博客。红黑树作为linux 内核里标准数据结构,在很多地方都用了。如进程虚拟地址空间,如网络部分。
红黑树
而组调度等只是更复杂的调度而已,能够进行组调度其根基在于调度实体,其嵌入到进程里面。
我不想进行更详细的代码分析,网上一搜一大堆,但是代码讲解并不能真正意义上的解释cfs是什么,也许还会陷入linux代码的泥藻里。 对于cfs,我只想记住三点,虚拟时间---无级变速,红黑树,调度实体间的层次关系。太多的东西真的记不住,即使记住了,如果不复习也会忘记的,倒不如记住最重要的几点,如果别人(面试等)问起我也可以答个大概。