本文通过详细分析老的调度器来说明cfs的优势。总是新理解新理解的,我怎么这么没完没了啊,其实每隔一段时间我都会在工作之余再读一读linux内核源代码的关键部分,每次读都有新的理解,然后就第一时间将心得记录下来,今天又读了cfs调度器,越来越发现其美妙了。这次配合了sched-nice-design.txt文档阅读,很受启发,万恶的调度程序终止了,新的时×××始了,O(1)调度器和CFS的作者都是Ingo Molnar,他真的是一个有破有立的家伙,太猛了!

O(1)调度器是很不错的,因为它的效率,正如它的名称一样,可是我们考虑的周全一点,操作系统对任务的调度不仅仅是用最快的速度选择最值得运行的进程,而也要兼顾最不值得运行的进程,所谓“效率优先,兼顾公平”。O(1)对于优先级调度的本意是很有效的,因为它可以在最快的时间内选择出优先级最高的进程,可是低优先级的进程可能因此饥饿而得不到执行的机会正如那篇sched-nice-design文档中讲的那样,O(1)调度器以及以前的更土的调度器的根本麻烦在于时间片的计算,因为那些调度器没有抽象到真正的的层次,更多的是利用底层的硬件中断来进行的,这样的话,根本无法用统一的方式来计算时间片,必须受累于底层的硬件,也就是说没有一个万能的时间片计算公式对每一台机器每一个系统都适用,比如一个系统的HZ定义为1000,另一个是100,那么这两个系统的调度行为就是不一样的;第二,老的时间片分时调度中的时间片更多的是一种绝对的参考值,一会会说出原因,现在要说的就是时间片计算公式计算出来的时间片和进程的优先级不是一个和谐的关系,比如线性关系,比如在nice值为+19到+15这之间,nice值每减少一个点其时间片平均增加delta1,而nice在0到4,时间片平均增加delta2,而这两者是不同的,这是不应该的,因为比如我想让时间片大大增加些,我必须知道我当前的nice值。

以上说的是总体上的,下面我来说说我的理解,到O(1)调度器加上更土的调度器,linux的调度器的时间片的计算大致分了三个阶段,更土的调度器里面也即是O(n),计算出来的时间片几乎分辨不出谁的优先级更高,优先级更高的进程的时间片也不会更具有优势,因为那时的调度器想把时间片限制在50毫秒的左右不超出多少的范围内,计算规则很简单:

#elif HZ < 1600

#define TICK_SCALE(x) ((x) << 1)

#define NICE_TO_TICKS(nice) (TICK_SCALE(20-(nice))+1)

这个简单的公式很线性,很和谐,但是计算出来的时间片几乎差不离,高优先级的进程没有什么优势,后来发展到了O(1)调度器以后,当然进行了改进:

#define BASE_TIMESLICE(p) (MIN_TIMESLICE + /

((MAX_TIMESLICE - MIN_TIMESLICE) * /

(MAX_PRIO-1 - (p)->static_prio) / (MAX_USER_PRIO-1)))

static inline unsigned int task_timeslice(task_t *p)

{

return BASE_TIMESLICE(p);

}

这个公式将最高优先级的时间片延长了,而把最低优先级的时间片缩短了,不管怎样增加了优先级时间片的动态范围,这个在效果上的表现就是,优先级高的进程表现出来的时间片更加长了,为了将优先级最高的进程的时间片和优先级最低的进程的时间片有效分离,增加时间片基数是一种方式,比如,最低优先级的时间片是10,然后每增加1个优先级则增加时间片n倍,取n为2,那么从低到高的时间片依次为10,20,40...,或者按照等差数列增加,比如差为10,那么就是10,20,30,40...但是会遇到一个问题,nice值或者优先级值的增加会和时间片的增加同步吗?如果我们强调同步,那么最高优先级的进程的时间片就会过于长,要么就是基数过小,这样的话就会引起频繁调度而刷新cache。后来在2.6.9内核往后的实现里,又有了新的策略:

static unsigned int task_timeslice(task_t *p)

{

if (p->static_prio < NICE_TO_PRIO(0))

return SCALE_PRIO(DEF_TIMESLICE*4, p->static_prio);

else

return SCALE_PRIO(DEF_TIMESLICE, p->static_prio);

}

新的实现采用了以nice 0为分界点,正值采用缓线性,负值采用陡线性,这可以保证在nice为正值的时候,nice值的减少率和时间片增加率同步,其实就是两个点的同步,就是+19的nice值和0的nice值,并且为了防止HZ为1000的系统中频繁调度,采用了约定最小时间片为5ms的方式硬性规定,可是为何选择+19的nice值考虑呢?因为它是一个极端值,两点确定一条直线,一个是0,一个是+19,可是为何采用两类斜率的线段呢?因为如果统一采用缓斜率的线段,那么会造成和2.4内核以及2.6.9以前一样的问题,动态范围小,高优先级不突出,但是如果统一采用陡斜率,那么就会造成高优先级的进程时间片过于长,这会引起系统响应性差,注意这在unix不是问题,可是对于linux就是问题了,因此为了既要实现突出高优先级进程,又要削弱低优先级进程又不至于削的更弱而引起频繁调度而刷新cache,那么就很“艺术”的采用了双斜率线性结构,这里有一个哲学,就是如果你既要抬高优势又要削减劣者,那么你要看看它们是否对称,如果不是,比如劣者有个显然的极端,那么在线性结构中,这个显然的极端值一定是一个定线段的端点,正如这里的5ms,另外一个端点就是缓陡分界点,还有一个就是最高的极值,这后面两个极值就需要经验值和别的约束条件了,为何使用线性结构呢?不为别的,正是因为它简单!我一直在想世界不是对称的,其实我们的世界就不是一个对称的,比如温度,我们知道-273°C,而高温已经到达很高的了,有没有高温极限呢?现在还没有找到,如果说我们人类是和谐,是美的化身,那么我们为何不生活在(-273+1000000000000000000000)/2°C呢?...要么我们否认-273是最低的温度,要么我们人类就不要再得瑟。闲话少说,又跑偏了...正是由于旧的调度器的时间片的双斜率导致了nice 0两边的不对称性,导致了你在当前nice为正值和负值两种情况下进行的nice系统调用的行为不同,一个+1时间片减少的快一个减少的慢。在那篇文档中,作者特意说了,最重要的就是linux想用统一的方式进行时间片计算去没有想到不可能,因为时间片的计算和HZ的定义密切相关,这就引入了一个变量,想完全线性的实现是不可能的。

不仅如此,就是因为老的调度器(呵呵,现在O(1)都成了老的调度器了)只选择最高优先级的进程而不管低优先级的进程,因此它是“少了一环”的调度,不能称为是完全的调度,正是它的不完全,才引入了很多复杂的外围算法,比如基于平均睡眠时间的交互检测算法,比如优先级的静态和动态分类算法,前面的一篇文章说过,O(1)调度器的延时减少了,减少的是寻找最高优先级进程的时间,别的时间并没有有效减少也不可能减少,因为一个进程需要占用确定的时间片,那么所有的进程完全运行完也将是确定的时间片,而系统的进程数量将使整个调度周期延长,这可能造成更大面积的饥饿现象存在,为了避免这种现象,不管是linux还是windows,都有很多动态算法,比如饥饿检测以及优先级临时提升等等,这些算法本身就造成了不多但是有的延时。新的CFS改变了这一切。

cfs很有创意,它囊括了关于调度的一切,包括优先级,包括寻找最值得运行的进程等等一切,在cfs中,以前O(1)的创意如今简单的成了一棵红黑树,红黑树本身就有优先级的性质,因为它是一棵排序树,而且插入和删除的效率很高,由于从运行队列删除进程很多都是发生了该进程运行时期,出队运行更是增加了效率,到此为此O(1)的所有工作,一棵红黑树就全部搞定了,就是简单的找出最高“优先级”的进程,在cfs中,没有了时间片,优先级对应成了weight,叫做权值,系统的运行时间完全在所有进程之间按照权值公平分配,公平性体现在每个进程都有一个虚拟时钟,各个虚拟时钟相互追赶,调度器总是选择虚拟时钟最慢的进程运行,前面文章说过,权值小的进程虚拟时钟走的快,比如它在1个tick就走了1个字,而权值大的进程虚拟时钟走的慢,比如10个tick走了一个字,cfs的创举是,它可保证一个虚拟时钟的速度比是1:10的进程对,那么它的权值比是10:1,怎么保证呢?在linux的cfs调度器中,有一个静态数组,叫做prio_to_weight,它有40个元素,其中对应0到39这40个优先级,每增加一个优先级值,那么它的权值减少十分之一,注意,如果开始一个进程的权值是10,优先级是10,那么它的优先级变成9以后其权值会是9吗?不!因为整个系统的运行时间不变,你减少了1/10的时间,那么其它的所有进程将会增加这些时间,因为一个进程减少1/10,别的进程增加1/10,那么这个进程将减少25%的时间,这就是这个数组的妙处,每一个元素都是后一个元素乘以1.25%。这样的话,整个系统的权值将是一个按比例缩减的,相应的,其虚拟时钟的速度将按照这个比例增加,虚拟时钟向前推进你完全可以理解成以前的时间片,只不过这个时间片不再是固定的了,而是动态的,比如一个进程运行了10个tick其虚拟时钟走了一个字,这时系统中又来了一个进程,那么这个进程就不再运行10个tick了,可能会变成9个,如果这个系统就剩下它一个进程了,那么它将一直运行,不再进行无用的--p->task_timeslice的操作。记住,一切都是按照比例进行的,这个比例就是那个数组的缩减比例。

我们看看cfs怎么解决之前调度器遇到的那些问题,第一,不再需要双斜率的线段,因为可以保证高权值的进程的“时间片”就是低权值的N倍,这是这个数组决定的,调度器会计算这个进程的权值占用系统当前所有进程的权值和的几分之几,然后就分配整个调度周期几分之几的“时间片”,这个机制之所以可以如此完美又有效的进行就是因为每个进程虚拟时钟的向前推进方式--互相追赶,调度器选择最慢的运行(推进的速度不同)。cfs中的所谓动态运行时间可以保证cpu永远不会浪费去做没有意义的事情,如果系统只剩下一个进程,那么调度器会把整个cpu全部给了这个进程,本质上,正如作者的话:"CFS百分之八十的工作可以用一句话概括:CFS在真实的硬件上模拟了完全理想的多任务处理器"。在“完全理想的多任务处理器“下,每个进程都能同时获得CPU的执行时间。当系统中有两个进程时,CPU的计算时间被分成两份,每个进程获得50%。然而在实际的硬件上,当一个进程占用CPU时,其它进程就必须等待。这就产生了不公平。cfs的调度器只知道把cpu分给当前的进程们,这种分配是按照进程的权值平均分配的,虚拟时钟只是实现这种公平的手段而已。cfs中的nice调用可以“放心”的进行,因为nice影响的进程运行的“时间片”只和相对值有关和绝对值无关。cfs中将优先级巧妙的转换成了权值这个概念,而权值又是那么巧妙的组织,好,非常好!

cfs如此统一的解决了那么多问题,统一就是美,简单就是美!cfs是一个完全的调度方案,而不仅仅是挑选第一个值得运行的进程的方案,这个意义上O(1)仅仅是一个挑选第一个进程的方案罢了。

转载于:https://blog.51cto.com/dog250/1274143

CFS调度器的思想的新理解相关推荐

  1. 用c语言实现对n个进程采用“短进程优先”算法的进程调度_为什么Linux CFS调度器没有带来惊艳的碾压效果?...

    文章转自公众号"人人都是极客" 但凡懂Linux内核的,都知道Linux内核的CFS进程调度算法,无论是从2.6.23将其初引入时的论文,还是各类源码分析,文章,以及Linux内核 ...

  2. (5)Linux进程调度-CFS调度器

    目录 背景 1. 概述 2. 数据结构 2.1 调度类 2.2 rq/cfs_rq/task_struct/task_group/sched_entity 3. 流程分析 3.1 runtime与vr ...

  3. 为什么Linux CFS调度器没有带来惊艳的碾压效果? | CSDN博文精选

    任何领域,革命性的碾压式推陈出新并不是没有,但是概率极低,人们普遍的狂妄在于,总是认为自己所置身的环境正在发生着某种碾压式的变革,但其实,最终大概率不过是一场平庸. 作者 | dog250 责编 | ...

  4. Linux进程调度 - CFS调度器 LoyenWang

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  5. Linux---进程调度及CFS调度器

    Linux的调度算法 (1)O(N)调度器 O(N)调度器发布于1992年,从就绪队列中比较所有进程的优先级,然后选择一个最高优先级的进程作为下一个调度进程. 优点:操作简单,便于理解. 缺点:时间消 ...

  6. Linux进程调度-CFS调度器原理分析及实现,懂了

    1. 概述 (1) Completely Fair Scheduler,完全公平调度器,用于Linux系统中普通进程的调度. (2) CFS采用了红黑树算法来管理所有的调度实体 sched_entit ...

  7. CFS调度器学习总结

    CFS调度器学习总结 CFS基本概念 CFS调度对象 CFS调度依据 CFS虚拟时钟 CFS调度过程 CFS数据结构 CFS调度代码 介绍CFS的基本概念和核心数据结构. CFS基本概念 comple ...

  8. [Linux][内核学习笔记]--CFS调度器

    文章目录 1. 进程的状态转换 2. 内核调度器的发展 3. 调度策略 4. 与调度相关的系统调用 5. 优先级 6. CFS调度器的实现 6.1 相关结构体 6.1.1 sched_entity 结 ...

  9. cpu调度的最小单位_Linux CFS调度器

    一直没有写过关于Linux内核调度器的内容,这几天被问起,简单的讲了讲,收到一堆challenge,这次决定做一个通篇总结方便自己整理思路. 要说Linux2.4和2.6最大的差异就在于CFS调度器的 ...

最新文章

  1. Web前端——字体规范
  2. 素数筛选以及优化分析
  3. 4~20mA电流输出芯片XTR111完整电路(转)
  4. Python爬虫入门五URLError异常处理
  5. 一年发表603篇论文、研究被引近3.9万次,学者操纵引文遭质疑
  6. MySQL 输入任何语句都提示You must reset your password using ALTER USER 解决方法
  7. PulseAudio多线程通信:pthread_cond_broadcast/pthread_cond_signal/pthread_cond_wait(九)
  8. DDOS攻击工具有哪些?怎么防御DDOS攻击?
  9. 新编C语言习题与解析,新编C语言习题与解析
  10. cpu vtx测试软件,推荐几个好用的检测电脑CPU是否支持(Virtualization Technology)虚拟化技术的工具-推荐实用小软件 -亦是美网络...
  11. 利用OBS推流抖音直播电脑屏幕或PC游戏
  12. 认识CodeSmith
  13. GDSOI 2016 T2 星际穿越
  14. 《5K入门级项目实战:好来屋在线影院》之第 9 战 —— 电影信息管理
  15. 你应该具有的富人思维
  16. 思考之——java为什么不能多继承
  17. Python基本语法学习小结
  18. 利用for循环打印实心棱形和空心棱形
  19. 微软CodePlex平台开源项目TOP10
  20. 比较实用的免费图标字库(转)

热门文章

  1. 遇到:ORA-27121: UNABLE TO DETERMINE SIZE OF SHAR...
  2. ShardingSphere(五) 公共表配置,实现读写改操作
  3. Golang sync.pool对象池
  4. Spring@Schedule定时任务源码解析
  5. java冒泡排序代码_JAVA
  6. golang 返回值问题
  7. vuex:弄懂mapState、mapGetters、mapMutations、mapActions
  8. ArrayList 实现原理及源码解析(jdk8 底层⽤的是数组)
  9. 一文理解设计模式--命令模式(Command)
  10. Spring Boot 中使用MyBatis Mapper方式(xml)