linux调度器(二)——CFS模型

2019-04-13 21:31发布

本系列文章阅读的core是:2.6.32-220
这里使用“模型”而不是“算法”是因为这东西实在不好用算法描述(但是它却运行得很好,包括性能)。
         核心思想: 把CPU总时间按运行队列的所有se的权重分配给每个se。每个se使用cpu的顺序由它们已使用的cpu虚拟时间(vruntime)决定的,已使用的虚拟时间越少,它在运行队列的位置越靠左,那么它再次被调度执行的概率也就越高。每个se每次占用cpu后能够执行的时间(ideal_runtime)也由它们的权重决定,并且保证在某个时间周期(__sched_period)内运行队列里的所有se都能够至少被调度执行一次。并且为了保证新进程能够获得合理的vruntime(不至于一开始太小导致长期占用cpu,以及不同组之间的迁移的标准化),在每个cfs_rq上维护一个min_vruntime,所有的se vruntime的调整按它自己的cfs_rq的min_vruntime为基准。
从上面的思想中,我们可以得到下面几个关系:
vruntime增长速率决定了进程被调度的次数,而这个增率又是由进程的权重决定的,权重越大(优先级越高,并且系统设定NICE_0_LOADvrumtime增率等待实际物理时间的增率),增率越慢;
保证每个进程被在一个周期内被调度至少一次的周期是由就绪队列的进程个数决定的(以及sched_latency_ns共同决定,见后面的解释);
每个进程每次占用cpu能够执行的时间是由上面的周期、它本身的权重及该运行队列总共的权重共同决定的,这个时间并不是代表进程获得cpu后就一定能够执行这么多时间,这个只是它的上限,当有其它的更低vruntime的进程进入队列的话,该进程就可以被抢占,那么它执行的时间就会少于这个ideal_runtime,但是由于它执行的时间少了点,所以它的vruntime步长也就变小了,所以会被放在红黑树的靠左的位置。
    假设我们有A,B,C三个进程,它们的权重分别是1,2,3(1的vruntime增率与实质物理时间一致),并且假设运行周期为18ms(这样每个进程的ideal_runtime分别为3ms,6ms,9ms,这里直接使用常量是因为对于真实的系统它也是在当运行队列的进程数超过一定数的时候才按正比来计算,当小于这个值的时候使用的就是一个常量),它们进入就绪队列的顺序就是A,B,C,为了便于理解,我们再假设它们进入时的vruntime都为0(其实这个由min_vruntime决定的,而min_vrumtime又由当前运行进程及当前队列最小的vrumtime共同决定,在每次update_curr的时候都会进行更新),这样A开始执行,假设A执行了2ms,那么此时它的vruntime也等于2,如果现在B进来,那么B的vruntime小于A,所以B抢占了A,当B也执行了2ms,那么它的vruntime等于1,这时C进来了,所以它也抢占了B(B被重新放到就绪队列里,不过因为它的vruntime为1,所以它被放在A的左边(之前)),此时C按调度周期它最长可以执行9ms,但是当它运行了>3ms后,它的vruntime>1,然后此时B进程就会抢占C,依此类推。
         是的,如果真的如上面那样执行的话,那么系统的大多数时间将用在上下文切换。显然当C运动了3ms之后,它可以接着运行直到它把它的周期9ms运行完再切换,因为系统已经保证了在18ms内每个进程都被调度一次,那么以至于多次切换后总共运行9ms,还不好一次让它运行完,即CFS保证的是周期内的公平,微观上不公平,不过也只有不公平,才会导致往公平的方向发展。在内核的实现上也是这样的,只有当进程运行完它的周期才一定会被抢占,否则还要判断它的执行时间,如果小于系统默认的最小设置值,也不会被抢占;在这种情况下(运行时间小于它的ideal_runtime)唯一允许抢占的条件是:最左边等待的vruntime(与当前进程的vruntime差值)大于当前进程的ideal_runtime(不知道为什么是这样一个条件:虚拟时间与物理时间的一个比较???这个过程在check_preempt_tick)。上面只是一个最基本的模型,它没有考虑到进程被创建时的vruntime初始化,进程被暂停,唤醒……下面我们就来详细看一下这些相应的过程。

参考文献:
http://blog.csdn.net/janneoevans/article/details/8125106