文章目录
1. 进程调度算法
调度的时机通常是以下的几种,即当以下的事件发生的时候就会进行调度:进程从就绪态转变为运行态,操作系统会在就绪队列中选择一个进程运行;或者是进程从运行态转变为阻塞态;在当前进程进入阻塞状态之后OS必须选择一个进程继续运行,此时就会发生调度;同样的,当当前的进程运行结束的时候OS也会使用调度程序选择一个进程运行;调度的原则就是尽量使CPU的利用率高,系统的吞吐率高;周转时间短;等待时间短;响应时间迅速;
1. 先来先服务算法(FIFO)
- 在了解各个算法之前,先来介绍几个概念,以及相关指标,首先就是吞吐量,我们可以把吞吐量理解为单位时间内CPU执行作业的时间,比如系统每小时完成的作业数量;接着就是周转时间,周转时间 = 完成时间 - 到达时间,最后就是响应时间 = 首次运行的时间 - 到达时间;
- 当计算机系统是多道程序设计的时候,通常就会有多个进程或者是线程同时竞争CPU;只要有两个或者是更多的进程/线程处于就绪状态的时候,这种情形就会发生。如果只有一个CPU可用,那么就必须选择下一个要运行的进程,在操作系统中完成选择的这一部分称为调度程序,该程序使用的算法称为调度算法;调度算法在不同的系统之中有不同的目标,但是通常来说都是实现更高的吞吐量,更低的周转时间,更快的响应时间以及更好的均衡性,但是它们往往不可以被同时满足,比如说如果是一个实时系统,那么在截止时间之前完后任务将会是很重要的,相比之下此时的响应时间就不是那么的重要的了,但是在交互式系统之中,用户都希望能够获得最快的响应时间,所以说在这种系统之中响应时间才是最重要的;
- 先来先服务算法是一个非抢占式的算法,当操作系统使用该算法进行调度的时候,进程将会按照他们请求CPU的顺序使用CPU,该算法会使用一个队列来维护程序在运行过程中的调度,该队列通常是由链表实现的,该链表上的节点就是一个就绪进程,当一个作业从外部进入系统之后,如果该作业是第一个进入系统的作业,那么他就会立即开始运行,并且会运行它所期望的时间长度,该作业不会因为运行时间太长而被中断,如果在该作业运行的时候有其他的作业也进入了系统,那么这些作业将会被放置于就绪队列的队尾(在很多情况下该队列是由链表实现的);当真在运行的进程被阻塞时,就绪队列中的第一个进程就会接着运行,当被阻塞的进程变为就绪的时候,他就会被放置在就绪队列的队尾,也就是排在所有进程的后面;该算法适用于批处理系统。
- 该算法的实现是非常简单的,要选取一个进程运行的时候只要从队列的头部移走一个进程即可,要添加一个新的作业或者是阻塞一个进程的时候,只需要把改作业或者是进程添加到队列的末尾即可;不过,该算法的缺点也是非常的明显的,那些耗时非常短的进程可能会被排到耗时非常长的进程之后,此时系统的平均周转时间就会变得很高,影响了系统整体的性能;
2. 最短作业优先算法(SJF)
- 该算法每次都会执行运行时间最短的作业,比如有下图三作业,分别是A, B, C,那么他们的平均周转时间将会是(10+20+120)/3=50秒,但是在相同的情况下,如果采用先进先出算法的话,他们的平均周转时间将会是(100+110+120)/3=110秒(假设A只比B和C稍微的早到了一小会儿,虽说这种假设略显牵强,但是这种情况还是会出现的),大大的降低了周转时间的大小,该算法可以应用于比较在意平均用户周转时间的场景,需要指出的是,当所有的作业几乎都是同时到达系统的情况下,最短作业优先算法将会是最优的算法,但是在其它的情况下最短作业优先算法将不再是最优的调度算法,而且如果在程序执行的过程中一直有比较短的作业源源不断的到达系统等待被调度,那么那些长作业可能永远也不会得到运行的机会,此时我们说这些长作业会被“饿死”;该调度算法同样适用于批处理系统;
3. 最短完成时间优先算法(STCF)
- SJF是一个非抢占式的调度算法,因此它会出现一些问题,比如在上述的例子中,如果B和C晚于A到达系统,那么SJF仍然会先执行A,然后才会继续执行B和C,在这种情况下,他们的平均周转时间将会是(100+(110-10)+(120-10))/3=103.33,只是非常糟糕的,所以我们可以向SJF添加抢占,称为最短完成时间优先(ShortestTime-to-Completion First,STCF)或者是抢占式最短作业优先(Preemptive Shortest Job First ,PSJF)算法,每当有新工作进入系统的时候,该算法就会在剩余的工作以及新工作中确定谁的剩余工作时间最短,然后调度该工作;该算法同样适用于批处理系统;
4. 高响应比优先算法(Highest Response Ratio Next, HRRN)
- 该算法会在进行调度的时候先计算各个进程的响应比优先级,把优先级最高的进程投入运行,响应比优先级的计算公式如下所示:
从这个公式可以看出,如果两个进程的等待时间相同的时候,要求服务的时间越短,响应比优先级就越高,也就是说短作业的进程更加容易被选中运行;如果两个进程的要求服务时间相同的时候,等待时间越长,响应比优先级就越高,并且进程的响应比优先级会随着等待时间的增加而增加,也就更容易被选中运行;所以这个算法兼顾了短作业以及长作业;同时也不会使进程等待过长的时间;
5. 时间片轮转调度算法(RR)
- 以上三个算法虽然是用于批处理系统,但是以上三个算法的响应时间都太长了,如今的很多PC机都是供用户使用的个人电脑,那么其中的操作系统就必须非常重视响应时间这一性能指标,这是如果OS的调度程序使用的是以上三个算法的话,用户对响应时间的要求肯定是得不到满意的,如果是FIFO或者是SJF,那么后到来的程序作业可能会必须一直等到前面的进程执行完毕才能开始运行,此时的响应时间就太长了,哪怕是抢占式的SJF也会出现这样的问题,为了解决这个问题,我们可以引入一种叫做时间片轮转的调度算法;
- 时间片轮转算法是一种最古老,最简单同时又是最公平的调度算法;在执行时间片轮转算法的时候,OS会为每一个处于就绪状态的进程分配一个可以使用CPU的时间段,该时间段就被称之为时间片;如果时间片在用完的时候进程还没有执行结束,那么OS会剥夺CPU并分配给另外一个进程;如果进程在时间片用完之前就已经执行完毕了,那么CPU会立即进行切换,从而使其他正在处于就绪态的进程开始执行。时间片轮转调度算法的实现非常简单,调度程序只需要维护一个就绪进程列表,当一个进程使用完它的时间片的时候,如果该进程还没有执行完毕,那么就将该进程移动到队列的末尾;
- 虽说时间片轮转算法实现起来非常的简单,但是时间片长度的选取是一个亘久不变的话题;我们知道,从一个进程切换到另一个进程的时是需要一定的时间进行事务管理相关的工作的:保存并装入寄存器的值以及内存映像,更新各种表格以及列表等数据结构,清除并重新调入内存高速缓存等;如果我们假设发生进程切换的工作(有时也称为上下文切换)需要1ms,时间片长度为4ms,那么当进程使用完4ms的时间片之后(假设进程在执行一个时间片之后它的工作还没有执行完),CPU将耗费1ms的时间进行上下文切换,因此CPU会浪费掉20%的执行时间用于管理上下文切换;
- 为了降低管理上下文切换所浪费的时间比率(也就是提高CPU的效率),我们可以将时间片设置为100ms,这时浪费的时间只占有1%,但是如果在一个非常短的时间间隔内如果同时有50个进程进入了就绪队列,此时如果CPU是空闲的,那么第一个到达的进程将会立即执行,如果每一个进程都充分利用了分配给它们的时间片,那么第二个进程必须得要等100ms的时间之后才能够获取CPU,而最后一个进程要等上足足5s的时间之后才能够执行,假想你打开一个应用或者是点击一个应用的按钮之后过了5s才得到响应,那么你很有可能会恼羞成怒;另一方面,如果我们将时间片的大小设置为大于平均的CPU突发时间,那么通常就不会发生抢占,因为此时在时间片耗费完之前多数进程都会完成一个阻塞操作,从而发生上下文切换,但是这种上下文切换不是因抢占而发生的,而是在逻辑上确实有需要的时候才发生的,所以说这个时候CPU的执行效率将会得到提升;由此我们可以得到如下的结论:时间片设置的太短的话会导致过多的上下文切换,降低了CPU的效率;而将时间片设置的太长的话又会导致对后到达的进程以及短进程的交互请求的响应时间边长。经实践表明,将时间片设置为20~50ms通常是一个比