在上文中我们说过,CFS调度策略主要是针对chrt命令显示的SCHED_OTHER范围的进程,实际上就是一般的非实时进程。我们也已经知道,这样的一般进程还包括另外两种:SCHED_BATCH和SCHED_IDLE。在CFS的实现中,集成了对SCHED_BATCH策略的支持,并且其功能和SCHED_OTHER策略几乎是一致的。唯一的区别在于,如果一个进程被用chrt命令标记成SCHED_OTHER策略的话,CFS将永远认为这个进程是CPU消耗型的进程,不会对其进行IO消耗进程的时间补偿。这样做的唯一目的是,可以在确认进程是CPU消耗型的进程的前提下,对其尽可能的进行批处理方式调度(batch),以减少进程切换带来的损耗,提高吞度量。实际上这个策略的作用并不大,内核中真正的处理区别只是在标记为SCHED_BATCH时进程在sched_yield主动让出cpu的行为发生是不去更新cfs的队列时间,这样就让这些进程在主动让出CPU的时候(执行sched_yield)不会纪录其vruntime的更新,从而可以继续优先被调度到。对于其他行为,并无不同。
SCHED_IDLE
如果一个进程被标记成了SCHED_IDLE策略,调度器将认为这个优先级是很低很低的,比nice值为19的优先级还要低。系统将只在CPU空闲的时候才会对这样的进程进行调度执行。若果存在多个这样的进程,它们之间的调度方式跟正常的CFS相同。
SCHED_DEADLINE
最新的Linux内核还实现了一个最新的调度方式叫做SCHED_DEADLINE。跟IO调度类似,这个算法也是要实现一个可以在最终期限到达前让进程可以调度执行的方法,保证进程不会饿死。目前大多数系统上的chrt还没给配置接口,暂且不做深入分析。
另外要注意的是,SCHED_BATCH和SCHED_IDLE一样,只能对静态优先级(即nice值)为0的进程设置。操作命令如下:
[zorro@zorrozou-pc0 ~]$ chrt -i 0bash
[zorro@zorrozou-pc0 ~]$ chrt -p $$
pid 5478's current scheduling policy: SCHED_IDLE
pid 5478's current scheduling priority:0
[zorro@zorrozou-pc0 ~]$ chrt -b 0bash
[zorro@zorrozou-pc0 ~]$ chrt -p $$
pid 5502's current scheduling policy: SCHED_BATCH
pid 5502's current scheduling priority:0
多CPU的CFS调度在上面的叙述中,我们可以认为系统中只有一个CPU,那么相关的调度队列只有一个。实际情况是系统是有多核甚至多个CPU的,CFS从一开始就考虑了这种情况,它对每个CPU核心都维护一个调度队列,这样每个CPU都对自己的队列进程调度即可。这也是CFS比O1调度算法更高效的根本原因:每个CPU一个队列,就可以避免对全局队列使用大内核锁,从而提高了并行效率。当然,这样最直接的影响就是CPU之间的负载可能不均,为了维持CPU之间的负载均衡,CFS要定期对所有CPU进行load balance操作,于是就有可能发生进程在不同CPU的调度队列上切换的行为。这种操作的过程也需要对相关的CPU队列进行锁操作,从而降低了多个运行队列带来的并行性。不过总的来说,CFS的并行队列方式还是要比O1的全局队列方式要高效。尤其是在CPU核心越来越多的情况下,全局锁的效率下降显著增加。
CFS对多个CPU进行负载均衡的行为是idle_balance()函数实现的,这个函数会在CPU空闲的时候由schedule()进行调用,让空闲的CPU从其他繁忙的CPU队列中取进程来执行。我们可以通过查看/proc/sched_debug的信息来查看所有CPU的调度队列状态信息以及系统中所有进程的调度信息。内容较多,我就不在这里一一列出了,有兴趣的同学可以自己根据相关参考资料(最好的资料就是内核源码)了解其中显示的相关内容分别是什么意思。