**

调度部分

**
1)调度是什么?
就是计算机通过一定的算法,决定任务执行的顺序.

2)调度有什么用?
实际应用中,计算机的资源是有限的.所以要通过合理的调度,使得任务执行的效率(用所有任务的平均完成时间来代表,即turnaround time),用户的体验(IO请求来到时,任务被交给IO设备的等待时间,即response time)等等多方面的性能尽可能地满足我们的需求.
这里需要指出的是,在不同的应用场景下,我们对性能的偏好是不一样的:
有时任务多是计算密集型,那么我们就要追求尽可能地利用CPU,以求尽快完成任务;
有时任务多是IO密集型,那我们就要追求能尽快让IO请求得到答复的调度……

3)调度怎么设计?
调度算法的设计,听起来令人头大,因为我们并不知道一般的任务是怎么样的,那设计算法又从何谈起呢?一般来说,解决问题的思路有两种:从一般到特殊,从特殊到一般.此时,我们只能从特殊到一般,我们先做一些特别的假设,让问题尽可能简单些:
1.每个任务所花的时间是一样的
2.所有的任务都在同一时刻来到
3.一旦任务开始,不完成绝不离开CPU
4.任务所用到的硬件只有CPU
5.每个任务所花的时间是一样的

为了衡量我们设计的算法,对于单个任务,我们使用Tturnaround=Tcompletion-Tarrival来衡量它的用时,用这个值的平均来描述我们算法的好坏.

  1. 最容易想到的调度算法,当然是First In, First Out, 即FIFO,有时也作FCFS.简单一点说,这个算法非常公平,即讲究先来后到,先到来的任务先享受CPU的服务,直到任务完成,下一个到来的任务开始执行……我们能使用这么简单的算法,是因为我们的假设让问题变得如此简单.
    那让我们去掉其中一条假设—"所有任务耗时相同”.假设此时A,B,C三个任务用时分别是100ms,10ms,10ms,它们同时到达,我们的调度器就有可能按照A-B-C的顺序安排它们执行,如下图7.2,那么平均的Tturnaround将会达到110ms,是一个非常差的水准.因为很明显按照B-C-A的顺序,Tturnaround就将会降低至50ms
    OSTEP读书笔记,调度部分上(第七,八章)
    OSTEP读书笔记,调度部分上(第七,八章)

  2. 针对上面的例子,我们有了一个想法—Shortest Job First(SJF),这样调度器就会按照B-C-A的顺序安排这三个任务了.事实上可以证明,在现有的假设下,SJF就是最好的调度方法.
    但我们的假设还是太强,于是我们再次去掉其中一条—“所有任务同时到达”.如此我们便会遭遇到如下图7.4中的困境:B,C任务耗时10ms,的确比A任务耗时短得多,但A任务却占了”先来后到”的便宜,占有了CPU,使得Tturnaround变成了糟糕的103.33ms.
    这时我们也许会想,如果在新任务进入队列时,重新给任务分配CPU如何呢?进一步,我们还是按照SJF的思想,给能最早完成的任务分配CPU如何呢?那么情形就变成了Figure7.5, Tturnaround变成了不错的50ms.
    OSTEP读书笔记,调度部分上(第七,八章)OSTEP读书笔记,调度部分上(第七,八章)

  3. 上面的思想,可以概括为”Shortest Time-to-Completion First”(STCF),即最早完成的任务最先.当然,为了能运用这个方法,又一个假设不得不被去除了—“CPU对任务要从一而终”.

  4. 可以看到,STCF又是一个所谓的”现有假设下最棒”的算法了.但我们的情形将会变得再复杂一点.这次我们要引入新概念—Tresponse.具体来说Tresponse=Tfirstrun-Tarrival,一个任务进入后的响应时间,等于等待第一次服务到来的时间.为什么它也要被列入考察名单呢?因为有很多用户,很多进程都在等待CPU的服务,你肯定不希望它们等得太久:想想你去餐厅里坐下半天却不见有服务生上来的场景吧!
    让我们用实际例子来说明,下面的第一幅图是用SJF来处理A,B,C三个进程,假设它们是在T=0时同时到来的,可以看到它们的平均等待时间是5ms( (0+5+10)/3 = 5).然而如果我们使用所谓的Round-Robin(RR)策略,即每次只运行一个任务一段时间,这段时间叫做”time slice”, 很显著地降低了Tresponse
    OSTEP读书笔记,调度部分上(第七,八章)OSTEP读书笔记,调度部分上(第七,八章)
    那就会有同学要发问了,这个time slice越短, Tresponse不就越短,就代表算法越出色么?这是个美好的幻想,因为我们此前没有考虑任务之间切换的成本.
    打个比方,我写一个小时数学作业,再写一个小时语文作业,行不行?当然可以,我们把桌子收拾干净,换换脑子,在书包里把另一科作业拿出来所用的时间t,在这一个小时面前是可以忽略的.但你要说,我写一分钟数学作业,写一分钟语文作业,行不行呢?这时前面提到的时间t就不可以被忽略了,因为它相比一分钟很长.
    计算机也是一样的,每次转换任务时都有一系列的收尾工作和准备工作:上一个任务带来的影响要更新到内存和磁盘中去吧?下一个任务的数据要还原到上次执行之后的样子吧?新任务如果有分支要进行分支预测吧?balabala很多的
    而且RR有自己的问题,就本例中SJF的Tturnaround不过是10ms而已,然而用RR得到的结果是14ms,非常糟糕.借用书中的原话”you can’t have your cake and eat it too”,鱼和熊掌不可兼得.

  5. 我们现在只剩下两个假设了—“任务只用CPU”,”任务用时是已知的”.很显然任务是需要用到其他设备的,没有I/O的电脑有什么意义呢?所以我们抛弃前一条假设,任务在CPU中发出I/O请求时,它就被堵塞住了,放弃了CPU的使用权而等待I/O的完成
    我们继续用具体的例子,A,B两个任务都需要50ms的CPU时间,但A每在CPU中运行10ms就需要10ms来处理I/O,B不会发出I/O请求.此前我们STCF只考虑任务在CPU中的运行时间,面对这个问题,如图7.8,A第一次放弃CPU后,A剩下的所需CPU时间少于B,故B不能利用CPU,A再次进入CPU……资源就被浪费掉了.我们理想中的情况应该是如图7.9,B利用好A放弃CPU的时间,怎么实现这一设想呢?其实我们只需要把A看成五个独立的需要10ms的任务就可以了,每次A放弃CPU时,调度器都会认为现在只有B一个任务处于队列之中,自然就会运行它了.换一个角度说,当一个任务发出I/O请求时,将它从任务队列中删除就是了.
    OSTEP读书笔记,调度部分上(第七,八章)OSTEP读书笔记,调度部分上(第七,八章)

  6. 现在我们只剩一条假设”任务用时已知”,但这也是最难移除的假设.移除了它,SJF和STCF就似乎毫无意义了.
    真的毫无意义么?
    显然不是,上面提到的两种方法给了我们一个很重要的启示:先运行短任务,有助于改善Tturnaround.
    第九章中介绍的算法MLFQ,就运用了这一点.MLFQ,是Multi-Level Feedback Queue的简称,听到这个名字我想很多人明白过来了,这个方法要借助许多队列来完成.
    MLFQ有一系列的独立的队列,每一个都有着自己的独一无二的优先级,优先级高的队列中的任务先运行,那么,同一优先级中有多个任务该当如何?它们将以RR的方式运行,用一个更规范的表示方法来表示就是:
    OSTEP读书笔记,调度部分上(第七,八章)
    一个新任务来到队列中时,调度会默认它的优先级比较高,如果它后来表现得经常与用户交互,那么它的优先级会停留在比较高的水平,这是为了让交互式程序得到更快的响应;如果它常常长时间使用CPU,那么它的优先级就会逐渐变低,这是为了让短的任务先运行,改善Tturnaround
    那么我们尝试用更加正式的形式来描述我们的优先级调整政策:
    OSTEP读书笔记,调度部分上(第七,八章)
    用形象的实例来解释,图8.2展示的就是一个CPU密集型的任务受到的对待
    OSTEP读书笔记,调度部分上(第七,八章)OSTEP读书笔记,调度部分上(第七,八章)
    你是否认为这样的策略已经很好了?或者你发现了其中可能存在的弊病?
    缺陷当然是有的:
    其一,当交互型任务比较多(too many)的时候,它们将联合起来大量消耗CPU时间,运算密集型的任务将饥渴难耐(starve);
    其次,聪明的或者说,淘气的开发者,将刻意在程序中插入大量的I/O请求,以使得自己的任务能停留在较高的优先级,从而优先得到执行(game the schedule),上图8.4中的灰色任务就这样做了;
    最后,任务并不是一成不变的,可能它前期是I/O密集型,此后就充斥大量运算呢?此前的策略优先级一旦下降就没法提升了.

  7. 正因上面提出的诸多问题,我们引入一条新规则:
    这个规则一下子就把两个问题解决了:
    CPU密集型的任务不再那么忍饥挨饿,之前如果I/O密集任务一直不完成或是有新的类似任务加入,低优先级的任务就没有机会执行完毕了,调整后,即使是慢一些,最终也能完成,详见图8.5(展示了有无该政策的情况);
    另一个好处是,能根据任务当前的特性动态地调整它的优先级;
    OSTEP读书笔记,调度部分上(第七,八章)OSTEP读书笔记,调度部分上(第七,八章)
    当然了,天下没有免费的午餐,新的问题出现了,究竟多长的时间间隔S(voo-doo constants出现了)安排一次优先级提升呢?太长,比较长的任务还是会饥饿;太短,交互密集型任务不能得到应得的CPU时间.

  8. 我们先解决历史遗留问题,如何避免利用调度器牟取长时间高优先级的行为呢?应该看到,这个问题的根源出在原来的规则4,所以我们可以大胆改动规则4,通过记录每个任务接受CPU服务的时间,只要时间到了一定的限度,就可以对它进行降低优先级的处理,规则化的书写如下:
    OSTEP读书笔记,调度部分上(第七,八章)
    那么,实例就如图8.6中所示的那样,左边是原规则4下的情形,右边是先规则下的情形,很明显两个任务得到的CPU服务更”公平”呢.
    也许正是因为MLFQ中还有许多问题,而不同的人对于这些不同的问题又会有不同的解答,有很多参数的设置更是只能通过实践来摸索,所以MLFQ产生了许许多多的版本:
    Solaris的MLFQ用一系列的表来决定一个进程生命周期里的优先级如何变化,时间片(time slice)又该是多大,管理人员可以通过改变表来定制调度器的表现.默认值是60个队列,时间片的长度由20ms逐渐增加至数百ms,优先级每1s提高一次……
    还有的MLFQ通过独有的公式来计算任务优先级的高低;
    还有的MLFQ的最高优先级是预留给操作系统的;
    还有的MLFQ留给用户nice指令供手动调节任务优先级……

我博客写的很少,自认水平也不高,写博客更多还是为了加深自己对问题的理解.如果文中存在问题,请您不吝赐教;如果您觉得我的文章对您起到了些许帮助,我感到非常荣幸

相关文章:

  • 2022-02-27
  • 2022-02-05
  • 2021-07-08
  • 2021-08-07
  • 2021-09-15
  • 2021-05-17
  • 2021-12-07
  • 2021-12-14
猜你喜欢
  • 2022-01-09
  • 2021-06-13
  • 2022-02-01
  • 2022-03-01
  • 2021-08-05
  • 2021-07-26
相关资源
相似解决方案