参考资料:《调度器笔记》Kevin.Liu

《Linux kernel development》

《深入Linux内核架构》

version: 2.6.32.9

下文中对于红黑树或链表组织的就绪队列,统称为用队列组织的就绪队列。                                              
    linux中用struct rq将处于ready状态的进程组织在一起。
    struct rq结构体包含cfs和rt成员,分别表示两个就绪队列:cfs就绪队列用于组织就绪的普通进程(这个队列上的进程用完全公平调度器进行调度);rt就绪队列用于组织就绪的实时进程(该队列上的进程用实时调度器调度)。
    在多核cpu系统中,每个cpu对应一个struct rq结构体实例。

核心调度器分为:
    1、周期性调度器  schedule_tick();
         周期性调度器不负责进程的切换,只是定时更新调度相关的统计信息,以备主调度器使用。
    2、主调度器      schedule();
         主调度器的工作是完成进程的切换,将CPU的使用权从一个进程切换到另一个进程。

调度器类:
    linux内核把用于处理普通进程调度的函数用struct sched_class结构体实例fair_sched_class组织起来;
    把用于处理实时进程调度的函数用struct sched_class结构体实例rt_sched_class组织起来;
    把用于处理idle进程调度的函数用struct sched_class结构体实例idle_sched_class组织起来。

linux中如何决定就绪进程在就绪队列中先后顺序的模型建立衍化过程:
A. runtime公平模型
    CPU的总时间按就绪进程数目等分给每个进程,每个进程在就绪队列中的先后顺序由它已享用的(runtime)决定,已享用CPU时间短的进程排在队列的最前面,反之排在队列的后面,调度器每次都选则队列的最前面的进程运行。
    漏洞:原有A、B、C三个进程在服务器上运行的runtime各自都等于1年,这时进程D被创建,那么接下来的一年内,只进程D在运行,其它进程如同dead一样……

B. min_runtime公平模型
    假设系统中有A、B、C三个进程,其已运行时间为:

---------------------|--------|----------|---------
进程 | A | B | C
---------------------|--------|----------|---------
runtime(ms) | 100 | 150 | 200
---------------------|--------|----------|---------

什么是“公平”???
    操作系统应该在“当前”,将时间公平分配给“当前”系统中的每个进程,“当前”意味着:
        a)进程A、B、C在系统中经历了100 + 150 + 200 = 450ms,它们应公平享用这段时间,即每个进程应当执行150ms。
        b)D进程被创建了,那么从现在起操作系统应该将CPU时间公平分配给这四个进程(从进程创建之时起,它就应该受到“不计其它进程的历史”的待遇,调度器对所有此后运行的进程一视同仁)。

如果将新建进程D的runtime设置为A、B、C中runtime最大的值显然对进程D不公,如果设置为它们中的最小值,那又对进程A不公。我们将进程D的runtime设置为A、B、C中最小的那个值,这就是在runtime公平模型上改进之后得到的min_runtime公平模型。

C. weight优先级模型
    不同进程具有不同的重要性,重要的进程尽量被分配多的CPU时间,不重要的进程应该分配少的CPU时间。
    为了达到这个目的,我们引入一个权重(weight)参数,即每个进程有一个权重值,进程得到的CPU时间和这个权重值成正比。
    假设进程A、B的权重分别是1,2,这就意味着:A进程执行了Nms后以及B进程执行2Nms后它们应当具有相同的先后顺序,即它们的runtime值基本相同。由于runtime表示进程已经运行的时间,显然和上述表述矛盾,因此我们引入另一个参数vruntime(虚拟运行时间)代替它(vruntime仅仅是一个数值,用来作为对进程进行排序的参考,不用来反映进程真实执行时间).
    每个进程有一个vruntime值,调度器总是选择vruntime值最小的进程使用CPU资源,并且vruntime增长的速度和weight值成反比.
    设单核处理器上有新建的A、B两个进程:
    a) 初始的时候两个进程都没有运行,runtime都等于0

-----------------|-------------|-----------
进程 | A | B
-----------------|-------------|-----------
weight | 1 | 2
-----------------|-------------|-----------
runtime(ms) | 0 | 0
-----------------|-------------|-----------
vruntime | 0 | 0
-----------------|-------------|-----------
按vruntime进行排序 | A B
-------------------------------|-----------

b) 调度器选择A,它运行了4ms:

  ----------------|----------|-----------
进程 | A | B
----------------|----------|-----------
weight | 1 | 2
----------------|----------|-----------
runtime(ms) | 4 | 0
----------------|----------|-----------
vruntime | 4 | 0
----------------|----------|-----------
按vruntime进行排序 | B A
---------------------------|-----------

c) B的vruntime最小,选择B运行,运行了4ms:

 -----------------|----------|-----------
进程 | A | B
-----------------|----------|-----------
weight | 1 | 2
-----------------|----------|-----------
runtime(ms) | 4 | 4
-----------------|----------|-----------
vruntime | 4 | 2
-----------------|----------|-----------
按vruntime进行排序 | B A
----------------------------|-----------

d) B的vruntime最小,选择B运行,运行了4ms:

  ----------------|----------|-----------
进程 | A | B
----------------|----------|-----------
weight | 1 | 2
----------------|----------|-----------
runtime(ms) | 4 | 8
----------------|----------|-----------
vruntime | 4 | 4
----------------|----------|-----------
按vruntime进行排序 | A B
---------------------------|-----------

e) vruntime相同,但是A在前,选择A运行,运行了4ms:

---------------|---------|-----------
进程 | A | B
---------------|---------|-----------
weight | 1 | 2
---------------|---------|-----------
runtime(ms) | 8 | 8
---------------|---------|-----------
vruntime | 8 | 4
---------------|---------|-----------
按vruntime进行排序 | B A
-------------------------|-----------

f) B的vruntime最小,选择B运行,运行了4ms:

  ----------------|---------|-----------
进程 | A | B
----------------|---------|----------
weight | 1 | 2
----------------|---------|-----------
runtime(ms) | 8 | 12
----------------|---------|-----------
vruntime | 8 | 6
----------------|---------|-----------
按vruntime进行排序 | B A
--------------------------|-----------

D、period模型
    早期调度器使用了时间片模型,例如每当4ms后(或着进程还未执行完4ms,就有特殊情况产生了,比如进程要睡眠),主调度器
schedule就会重新选择一个vruntime值最小的进程来执行。
    但是现在我们不用时间片的概念了,那么主调度器schedule应该在什么时候启动并选择一个新的进程执行呢???
    
    引入period参数
    系统设定一个period值(它表示一段时间),每个进程对应一个ideal_runtime值(称为理想欲运行时间),每个进程的ideal_runtime值的设定方式:所有可运行进程的ideal_runtime值的和等于period,每个进程的ideal_runtime值的大小与它的权重weight成正比。
    该模型规定:每个进程每次获得CPU使用权,最多执行它对应的ideal_runtime这样长的时间。
    注意:CPU并没有把时间分成长度为period的时间段,系统仅仅限定了每个进程每次执行时间不能超过它对应的ideal_time指定的时间长度。
    如果period=20ms,当前系统中只有A、B、C、D四个进程,它们的weight分别为:1、2、3、4。那么A的ideal_runtime = 2ms,B,C,D的ideal_runtime依次为4ms,6ms, 8ms。

a) 初始情况如下:

--------------------|-----------|----------|----------|----------
进程 | A | B | C | D
--------------------|-----------|----------|----------|----------
weight | 1 | 2 | 3 | 4
--------------------|-----------|----------|----------|----------
ideal_runtime(ms) | 2 | 4 | 6 | 8
--------------------|-----------|----------|----------|----------
runtime(ms) | 0 | 0 | 0 | 0
--------------------|-----------|----------|----------|----------
vruntime | 0 | 0 | 0 | 0
--------------------|-----------|----------|----------|----------
按vruntime进行排序 | A B C D
--------------------|--------------------------------------------

b) 和前一个模型一样,vruntime的行走速度和权重值成反比,设定权重权为1的A进程的vruntime和实际runtime行走速度相同。A先执行,它执行了2ms,此时:

--------------------|-----------|----------|----------|----------
进程 | A | B | C | D
--------------------|-----------|----------|----------|----------
weight | 1 | 2 | 3 | 4
--------------------|-----------|----------|----------|----------
ideal_runtime(ms) | 2 | 4 | 6 | 8
--------------------|-----------|----------|----------|----------
runtime(ms) | 2 | 0 | 0 | 0
--------------------|-----------|----------|----------|----------
vruntime | 2 | 0 | 0 | 0
--------------------|-----------|----------|----------|----------
按vruntime进行排序 | B C D A
--------------------|--------------------------------------------

c) B的vruntime值最小,选择B运行,假设B运行了3ms:

--------------------|-----------|----------|----------|----------
进程 | A | B | C | D
--------------------|-----------|----------|----------|----------
weight | 1 | 2 | 3 | 4
--------------------|-----------|----------|----------|----------
ideal_runtime(ms) | 2 | 4 | 6 | 8
--------------------|-----------|----------|----------|----------
runtime(ms) | 2 | 3 | 0 | 0
--------------------|-----------|----------|----------|----------
vruntime | 2 | 1.5 | 0 | 0
--------------------|-----------|----------|----------|----------
按vruntime进行排序 | C D B A
--------------------|--------------------------------------------

d) C的vruntime值最小,选择C运行,假设C运行了3ms:

 --------------------|-----------|----------|----------|----------
进程 | A | B | C | D
--------------------|-----------|----------|----------|----------
weight | 1 | 2 | 3 | 4
--------------------|-----------|----------|----------|----------
ideal_runtime(ms) | 2 | 4 | 6 | 8
--------------------|-----------|----------|----------|----------
   runtime(ms) | 2 | 3 | 3 | 0
--------------------|-----------|----------|----------|----------
     vruntime | 2 | 1.5  | 1 | 0
--------------------|-----------|----------|----------|----------
按vruntime进行排序   | D C B A
--------------------|--------------------------------------------

e) D的vruntime值最小,选择D运行,假设D运行了8ms:

 --------------------|-----------|----------|----------|----------
进程 | A | B | C | D
 --------------------|-----------|----------|----------|----------
weight | 1 | 2 | 3 | 4
--------------------|-----------|----------|----------|----------
ideal_runtime(ms) | 2 | 4 | 6 | 8
--------------------|-----------|----------|----------|----------
runtime(ms) | 2 | 3 | 3 | 8
--------------------|-----------|----------|----------|----------
vruntime | 2 | 1.5 | 1 | 2
--------------------|-----------|----------|----------|----------
按vruntime进行排序  | C B A D
--------------------|--------------------------------------------

f) 进程D运行的时间等于它的ideal_runtime,调度器被激活,重新选择一个进程运行,接着C进程被选中执行。
       关键在于: C可以运行多长时间???

根据ideal_runtime的定义,它只是要求,每个进程每次占用CPU的资源不超过它对应的ideal_runtime,上次进程C被调度的时候它只执行了3ms,没有超过它的ideal_runtime(6ms);但是,这次它又可以获得CPU的使用权了,是新的一次调度了,与之前无关。因此,进程C最多可以运行6ms,那么接下来进程C可以连续运行6ms:

 --------------------|-----------|----------|----------|----------
 进程 | A | B  | C | D
--------------------|-----------|----------|----------|----------
weight | 1 | 2 | 3 | 4
--------------------|-----------|----------|----------|----------
ideal_runtime(ms) | 2 | 4 | 6 | 8
--------------------|-----------|----------|----------|----------
runtime(ms) | 2 | 3 | 9 | 8
--------------------|-----------|----------|----------|----------
vruntime | 2 | 1.5 | 3 | 2
--------------------|-----------|----------|----------|----------
按vruntime进行排序  | B A D C
--------------------|--------------------------------------------

不要错误地认为:系统将CPU时间划分成一段一段的,每片长度为period,并且将它们按权重分配给每个进程,并且规定它们在该period内最多执行ideal_runtime限定的时间,进入下一个period时间段后,系统又重新为各个进程分配ideal_runtime;    
因为CPU并没有把时间分成长度为period的时间段,系统仅仅限定了每个进程每次执行时不能超过它对应的ideal_time指定的时间长度。

该机制的作用是:每个进程在就绪队列中等地的时间不会超过period,因为每个进程获得CPU使用权后,如果它执行的时间等于它的ideal_runtime,那么它的vruntime基本上就比其它所有进程的vruntime值高了,自然会排到队列的后面。

上述基于weight优先级模型和period模型的调度器所实现的效果, 每个进程每次调度运行的时间不在受4ms(例如)的限制了,而是可以运行“任意”长时间:
    a) 每个进程每次获得CPU使用权最多可以执行与它对应的ideal_runtime那么长的时间。
    b) 如果每个进程每次获得CPU使用权时它都执行了它对应的ideal_runtime那么长的时间,整个就绪队列的顺序保持不变。
    c) 如果某个进程某几次获得CPU使用权时运行的时间小于它ideal_time指定的时间(即它被调度时没有享用完它可以享用的最大
时间),按照vruntime进行排序的机制会使得它尽量排在队列的前面,让它尽快把没有享用完的CPU时间弥补起来。

period抽象模型基本上就是对内核cfs调度机制的一个抽象(没有考虑睡眠,抢占等细节):
        a) 每个进程有一个权重值(weight),值越大,表示该进程越优先。
       
b)
每个进程还对应一个vruntime(虚拟时间),它是根据进程实际运行的时间runtime计算出来的。vruntime值不能反映进程执行的真实时间,只是用来作为系统判断接下来应该选择哪个进程使用CPU的依据————调度器总是选择vruntime值最小的进程执行。
        c) vruntime行走的速度和进程的weight成反比。
       
d)
为了保证在某段时间(period)内每个进程至少能执行一次,操作系统引入了ideal_runtime的概念,规定每次获得CPU使用权时,执行时间不能超过它对应的ideal_runtime值。达到该值就会激活调度器,让调度器再选择一个vruntime值最小的进程执行。
        e) 每个进程的ideal_runtime长度与它的weight成正比,如果有N个进程,那么:
                                           task[i]->weight
           task[i]->ideal_time = -------------------- * period
                                         sum_weight(task, N)

转自:阿加

CFS理论模型的更多相关文章

  1. linux cfs调度器_理论模型

    参考资料:<调度器笔记>Kevin.Liu <Linux kernel development> <深入Linux内核架构> version: 2.6.32.9 下 ...

  2. O(1)调度器的时间计算公式与CFS调度器

    http://blog.csdn.net/dog250/article/details/48750809 O(1): 优先级计算: 进程优先级公式:prio=MAX_RT_PRIO+nice+20其中 ...

  3. linux 提高进程优先级nice+ 进程调度CFS

    http://www.cnblogs.com/wang_yb/archive/2012/09/04/2670564.htmlhttp://liwei.life/2016/04/07/linux%E7% ...

  4. CFS: 虚拟运行时间

    http://edsionte.com/techblog/archives/4331 nice和prio的关系如下: #define NICE_TO_PRIO(nice) (MAX_RT_PRIO+n ...

  5. 进程的优先级 与 CFS 进程调度

    在Linux下改变进程的优先级 作者:曾老师,华清远见嵌入式学院讲师. 作为多任务的操作系统,Linux内核为每个创建的进程分配时间片并根据其优先级进行调度.当进程被创建时,其对应的task_stru ...

  6. 朴素UNIX它-Linux CFS注视

    该系列产品,被称为纯UNIX,但它也包含各种类别UNIX该系统的细节,自从完成我多年的学习笔记本系列文章,分析了各种UNIX,类UNIX思想和情感的实现. 这篇文章是比较短.只是分析Linux CFS ...

  7. Linux 2.6 完全公平调度算法CFS(Completely Fair Scheduler) 分析

    转会http://www.ibm.com/developerworks/cn/linux/l-completely-fair-scheduler/index.html? ca=drs-cn-0125 ...

  8. cfs

    转自:http://www.cnblogs.com/openix/p/3254394.html 下文中对于红黑树或链表组织的就绪队列,统称为用队列组织的就绪队列.                    ...

  9. linux内核cfs浅析

    linux调度器的一般原理请参阅<linux进程调度浅析>.之前的调度器cfs之前的linux调度器一般使用用户设定的静态优先级,加上对于进程交互性的判断来生成动态优先级,再根据动态优先级 ...

随机推荐

  1. tomcat8.5打开manager页面报错的问题

    之前用的8.0版本的tomcat,最近需要将版本升级,当前8的最新的版本是8.5.42,升级之后发现manager页面打不开了,就是下面这个按钮的页面 点击之后报403没权的错误 还是按照8.0版本的 ...

  2. 微信小程序开发(三)点击事件

    接着上篇博客继续. 如下修改: // index.wxml <view>Hello World!</view> <button bindtap="but&quo ...

  3. LeetCode3.无重复字符的最大子串

    给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: "abcabcbb"输出: 3 解释: 因为无重复字符的最长子串是 "abc&quo ...

  4. Pursuit For Artifacts CodeForces - 652E (Tarjan+dfs)

    Pursuit For Artifacts CodeForces - 652E Johnny is playing a well-known computer game. The game are i ...

  5. 浅入深出Vue:登录

    上一篇我们实现了注册功能,现在我们来实现一下登录功能. 准备工作 新建登录组件 添加登录组件的路由对象 新建登录组件 Login.vue: <template> <div> & ...

  6. IQ基础

      I: in-phase 表示同相Q: quadrature 表示正交,与I 相位差90 度. 现在来解释IQ信号的来源: 最早通讯是模拟通讯,假设载波为cos(a),信号为cos(b),那么通过相 ...

  7. C#学习之Timothy Liu

    原文出自 本文摘录了一些值得学习的地方. 1.对一个程序来说,静态指编辑期.编译期,动态指运行期. 静态时装在硬盘里,动态时装在内存里. 2.反射示例 通过反射获得类的属性和方法. static vo ...

  8. Zabbix Agent 安装指南和 Zabbix Server 设置自动发现

    Zabbix Agent分为两种模式,被动模式(Passive)和主动模式( 我们实验在node1.yulongjun.com 和node2.yulongjun.com上分别配置Zabbix Agen ...

  9. 虚拟dom比对原理

    dom对比步骤 1.用js对象来表达dom结构 tagName 标签名props 元素属性key 唯一标识children 子元素,格式和父元素一样count 有几个子元素,用于计算当前元素的索引,处 ...

  10. docker下安装运行mysql的过程以mysql5.7为例

    一.查找mysql资源 docker search mysql 其实这步顶多是看看有哪些mysql资源,除非你自己commit过一个特定的版本,否则直接执行下一步 二.安装mysql docker p ...