OS.StudyLog.Ch8.CPU Scheduling.CPU调度
- CPU调度的概念
-
- 调度时机
- 调度原则
- 一般系统中的调度算法
-
- FCFS.先来先服务
- SJF.短作业优先
- HHRN.最高响应比优先
- Round Robin(RR)轮循
- MFQ多级反馈队列
- FSS公平共享调度
- 实时调度
- 多处理调度与优先级反转
- 优先级反转
CPU调度的概念
进程都希望占用CPU去执行自己的工作,这就牵扯到上下文切换的知识。那么什么时候切,根据什么原则切,就与CPU调度相关了。
CPU调度:从就绪队列中选择一个进程/线程作为CPU将要运行的下一个进程/线程。
调度时机
和运行相关的状态变化——从一个状态到另一个状态,变化时会触发一次调度。
大的情况我们是调度应用程序,对于应用程序而言在运行时以用户态的进程存在,当我们启动一个进程,从开始到结束他中间不允许被打断(早期设计),称为非抢占式调度策略。这种策略可以确保进程一旦启动,其他进程无法打断它;但是也会导致其他进程不得不等待它执行完毕才能执行,也就意味着如果该进程因某些原因处于阻塞态,则其他进程仍需要等待加载,效率上可能不高。
另一种情况,抢占式调度策略。OS来决定某个时刻打断当前用户进程的执行,然后选择另一个进程去执行。
策略针对的是用户态的进程,内核里不存在抢占。当一个用户进程执行系统调用,如果这次系统调用在内核中不会导致该进程处于阻塞态(仍处于运行态),那么当这个系统调用正常返回时,一定会返回到发起这个调用的进程去继续执行,也就意味着内核中不会出现抢占现象。不会在内核中切换到另一个状态去执行,只要进程通过系统调用在内核中执行这个过程中,自身不会出现运行态到阻塞态的变化,可以确保返回时一定是发起请求的进程继续执行,这就是内核的不可抢占。
如果说内核中允许去抢占,那么打断当前进程,内核切换到另一个优先级更高的进程去执行,系统调用返回时,在内核态访问用户态时可能会变到另一个进程去,这种情况称为抢占式内核。
抢占和非抢占对用户态和内核态不同环境下的情况不同代表了操作系统的不同设计方法。之前的OS大多不支持用户态抢占,早期WIN系统不支持用户态抢占。
调度原则
进程在计算机系统中运行时的状态:
进程更多情况下是访问IO/内存,让CPU计算等。从计算机系统层面来看,以CPU为例,如图所示。在某个时刻CPU占用率高,在做复杂/频繁的计算;某个时刻骤降,可能进程在做IO操作,硬盘写入内存时,CPU等待。
我们希望尽量在某一个进程等待IO操作完成时,其他就绪态的进程开始执行,使得CPU尽量忙,CPU吞吐率尽量高。CPU越忙意味着利用的越充分。
评价指标:
这一系列的指标都可以做出对效率参考。
1.CPU利用率越高,我们认为整体的系统效率越好。
2.CPU是一方面,可能不能直接对应到应用程序的执行情况——比如web server,单位时间内完成事件数量,称为吞吐量,吞吐量越高意味着进程的效率越好。如果一堆进程吞吐量都很高,我们可以说调度算法好。
3.周转时间是从进程启动(创建)到结束。启动完之后会等待一段时间才会被CPU执行,执行也需要时间。也就说周转时间=等待时间+执行时间。我们希望等待时间越小越好。
4.等待时间:与上面的等待时间不同。这里是处于就绪态的进程等了多久才变成运行态去执行;而不是等处于阻塞态的进程等了多久才被唤醒。那么我们希望处于就绪态的进程等待时间越短越好,这样可以更快去占用CPU执行。
5.响应时间:从外设/用户发出请求,到这个请求被进程处理完毕。响应时间越快越好,特别是对于交互式应用来说,比如键鼠。
这些指标都可以用来评测分析掉段算法,可以量化分析。
一般而言,我们希望进程提供的服务更快而不太在乎其他性能指标。这里的快是比较含糊的,刚才的哪些指标里,如果你想满足一个指标可能就会对其他指标造成影响,它们之间相互矛盾,所以我们希望找一个平衡点来设计算法。
例子)水管:
喝水时,打开水管立刻出水,水流量不大但是可以立刻喝到。响应时间短,吞吐量小
浇花时,打开粗水管,水流量大,但是计算水在水管里的流动时间和落地时间,整体偏慢。吞吐量大,响应时间长
对于一个调度算法,我们希望一个算法的效果:
1.希望提出请求马上得到反馈
2.波动小,像开车一样。踩下刹车一会立刻急刹车一会猜半天没反应,从整体来看更加危险,不如固定踩下后固定时间后急刹,对于操作者有一个更好的可预测性和习惯性适应。
3.吞吐量越大越好。传文件很快传完。
4.等待时间越小越好,希望程序一启动立刻执行。
我们很难同时满足这些指标,在设计调度算法时,如果要考虑两个以上的性能指标,就需要做一些折中处理,无法都做到极致。所以我们选择在不同的场合选择最佳的体验方式。
比如交互系统,则对低延迟要求比较高;数据中心的服务器,则更需要的是吞吐率,需要提供大带宽(比如下载中心)。也就说根据不同需求设计不同的调度算法。
Linux针对桌面的调度和对针对服务的调度是不一样的,在不同应用需求下选择更合适的调度策略算法。
调度算法的重要衡量指标:公平
计算机系统里面有很多进程,要确保这些进程都能得到CPU的服务。不能给某一个进程很多CPU时间和某些进程很少的CPU时间。OS通过调度算法确保每一个进程能够公平得获得CPU时间。要求占用CPU时间和等待CPU时间都是大致公平的。基于公平性,也会有一些其他的调度算法使得多用户和多进程的计算机内公平分配计算机资源。
一般系统中常用且简单的调度算法:
FCFS.先来先服务
如图所示,根据进程到达CPU的顺序执行,不考虑任何因素,只根据先后顺序调度。
考虑调度衡量指标:平均响应时间
周转时间=等待时间+处理时间
对于P1:等待0 处理12
对于P2:等待12 处理3
对于P3:等待15 处理3
实现非常简单:谁先来就放入队列头。实现容易。
前面的进程执行时间越长,后面的进程等待时间越长。该算法会因为前面进程过长的执行时间影响整个系统效率。如果P3是一个交互式进程,要求低延迟低响应时间,该情况下则会大大影响整体效率和后面程序的运行。而且没有考虑抢占,在响应时间上也会受到很大影响。
SJF.短作业优先
优先考虑执行时间短的进程去占用CPU时间执行。
根据FCFS改进,进程执行时间决定了调度的优先级。如图,上图四个进程根据其执行时间排序,最短的在头部。
如果第一个进程正在CPU中执行,第二个进程已经到达CPU且其执行时间比当前执行的进程还要短,则有两种处理方式。
1.SJF/SPN非抢占式:不打断当前进程,继续执行第一个进程,新来的进程进入就绪队列等待第一个进程执行完毕
2.SRT抢占式:打断当前进程,立刻执行第二个进程,第一个进程剩余的执行时间与后续到来的其他进程比较并排序进入就绪队列。当前进程从运行态变为就绪态,挂回到就绪队列,当前新进程占用CPU执行。
优点:最小的平均等待时间
ci是指的进程的执行时间,如果把P3调到后面,就会发现在原公式基础上,分子变得更大了,也就说平均周转时间会比不换顺序要大。
优先考虑短任务,会出现的问题:
如果系统运行过程中产生了新进程且新进程均为执行时间较短,且如果不断的产生这些短进程会导致原本在就绪队列中的进程一直得不到CPU去执行。产生饥饿现象,有违公平原则。
而且还有一个很大的限制,调度进程时我们要预先知道进程的执行时间,但是一般而言启动一个程序很难去精确得知什么时候结束。
解决方法:预估执行时间。根据执行历史和一段时间内的执行情况,去推断下一个t时刻内会执行多久。
HHRN.最高响应比优先
与前面的算法相比,多考虑了一个等待时间。SJF考虑了执行时间但是没有考虑等待时间,该算法就是SJF的改善,可以防止饥饿。
缺点:不支持抢占,仍然存在预估未来的问题。
Round Robin(RR)轮循
使用时间切片和抢占来轮流执行任务,不像其他算法,该算法考虑了公平性。
我们希望这些进程可以轮流占用CPU。过程如图所示。通过这种方式使得每个进程都有机会去占用CPU执行。如果执行时间小于时间片,则执行完立刻切换其他进程。
计算等待时间:
P1:48+24=72
第一个时间片,开始即执行,0
第二个时间片,P4执行结束时刻 − - − 第一个时间片结束时刻 = = = 68-20=48
第三时间片,P4第二次执行完毕的时间 − - −第二次P1执行完毕的时间 = = = 112-88=24
其他同理。
RR算法相对前面的算法有比较多的上下文切换,上下文切换开销大。
如果时间片切换的过小,则切换过于频繁,开销增大。时间片太大则可能退化成FCFS。
也就说设计RR时间片大小是一个比较讲究的问题。经验规则是上下文切换的开销处于1%内。99%内用于实际进程执行。
时间片大小对整体的影响:
因为FCFS没有频繁切换,所以整体时间可能会更低。这里存在一个权衡问题,因为FCFS达不到每个进程的及时响应,也就说公平性会带来一些代价,但是未必是不必要的。而且FCFS根据先入顺序的不同性能方差极大,性能过多的取决于运气。
多级队列:
为了兼顾这两方面的问题,我们可以设计一个多级队列。把就绪进程分为多个队列,不同队列采用不同的调用算法,比如在最高优先级队列用SJF,优先级比较低的进程放入FCFS队列。高优先级队列的所有进程执行完毕后才会调度低优先级队列的进程。
这里仍然存在一个问题,不同优先级的进程一开始就划分了级别。但是进程是动态执行的过程,可能某个阶段侧重交互性某个阶段做大量计算处理,每个阶段特点不一样。考虑到进程执行特点来动态调整队列的级别。
MFQ多级反馈队列
公平和动态变化同时强调,考虑了进程的等待时间和执行时间,会根据进程的动态执行来动态调整。
反馈体现在进程执行的动态性可以在队列中的位置有所反应。比如:一个进程一开始是一个交互进程,一开始设立优先级比较高,前面IO访问后有大量等待时间,随着等待时间变长优先级就可以不断提高,把它往优先级高的队列上提。如果说过交互后需要做大量计算,可能就会占用CPU时间比较长,CPU密集型,这时候可能他需要很长的一段时间,时间段有的会很快,所以每用完一次时间片就降级(降到下一个优先级的队列去),用的时间片越多,设置的优先级越低。使得占用CPU时间很长的计算型进程会随着执行逐渐落入低优先级就绪队列,使得交互性比较好的进程迅速进入CPU去执行。
也就说等待时间越长,优先级越高;执行时间越长,优先级越来越低。这种反馈机制就得到了体现。能够区分进程在动态执行中的特征来动态调整。从而使得IO密集型的任务可以很快执行,CPU密集型任务可以逐渐去执行。我们希望交互性多的进程得到及时响应,而需要消耗大量计算机资源的进程可以慢一点。
FSS公平共享调度
专门强调公平性的调度算法,共享强调的是CPU的执行时间和等待时间。
前面的算法考虑公平性,但是没有把公平性作为一个非常重要的指标。该算法是公平性第一的算法。功能强计算机系统有一些面向服务器需要多人多用户分时共享。那么共享就有一个问题,某些用户开了很多进程,有些用户开了一个进程。我们需要能够在用户这个级别实现公平调度,对于一个用户而言,用户拥有的进程个数是不同的,那么确保用户层面上做到CPU资源的公平占用就是该算法的考虑重点。
一个调度算法在运行过程中除了受到理论讲到的上下文切换之类的影响之外,和硬件系统本身也有很大联系。
实时调度Real Time 系统,实时系统更多用于工业控制。比如火车、机床、嵌入式工厂的一些控制环境里,需要确保某些任务在规定时间内必须完成。
Real Time就是将来的某一段时间内,必须完成。时间是确定的,我们希望OS在跑应用时,要满足确定性和可预测性。
实时可以分为两部分。硬/软实时
硬实时:当系统中的某一个关键任务不能在规定时间内完成时,会产生灾难性后果,要求必修完成该任务。比如控制水坝。
软实时:要求规定时间内尽量完成某一任务。比如视频,我们希望视频每秒六十帧,达不到会导致体验不佳,但是没有灾难性后果。
绝对截止时间:必须完成的时刻
相对截止时间:在实时系统里,任务是间隔时间段内完成的。每个时间段内就要完成一个特定的任务。
因为是重复执行,所以有周期。执行时间就是蓝色区域,e表示。p是截止时间。也就说使用率低的话,更多的时间是处于休眠状态。
静态优先级调度算法:在任务执行之前,就已经确定了优先级。按照优先级,来选择任务,在规定时间内去完成。
动态优先级调度:任务优先级随着执行过程会动态变化,动态变化可能在不同时刻有区别,所以就可能会延迟调度和提前调度。
典型的两类调度算法:
RM:静态。EDF:动态,随着任务执行,任务离着deadline越短,优先级越高。
多核处理器的情况下,有效利用CPU完成并行调度。相对前前面的调度需要考虑的问题:
1.因为拥有多个CPU,那么来了一个新的进程后,将这个进程放到哪一个CPU去执行。
2.存在多个CPU,可能存在有的忙有的闲,如何确保大致均匀分配,要追求负载平衡,多处理情况下不要有闲置处理器。
调度算法本身需要动态探测其他CPU忙碌情况来完成负载平衡。
正常情况下按照优先级顺序来,但是如果T1任务没有及时完成,OS会认为系统处于不稳定状态,就会重启系统。
原因:
图片看法:在时间段内,T123三条线上只会有一条线上有方格。
也就说某时间段内,哪项任务在执行。
一开始T1未加载,T3执行,并且访问了一部分共享资源,此时被T1抢占。T1在执行一段时间后,需要用到T3访问的这块共享资源,但是此时T3由于还未对这部分资源做完处理且T2加入,T2优先于T3执行。
也就说T1虽然优先级最高,但是T1执行需要等待T3,而T2又占用了T3的时间,所以T1也受制于T2的时间。从而出现T1无法及时完成任务,导致系统不稳定,重启。
解决:
优先级继承:
T3访问了一个共享资源,如果T1执行过程中也需要访问这个共享资源,那么T3的优先级会动态提升,提升到和T1的优先级,此时如果T2再来,则不会被抢占,T3可以尽快执行完这块共享资源处理。通过低优先级进程继承高优先级进程的优先级,导致中级优先级继承无法抢占低优先级继承。
一开始做一个统计,统计进程需要的资源,把资源也定一个优先级。资源优先级等于最高优先级继承的优先级。接下来进程执行过程中,占用了资源执行,优先级得到提升,等于资源的优先级,其他进程除非优先级超过所占用资源的优先级,否则就要等待。通过这种方式,确保一旦访问共享资源,可以迅速完成共享资源的操作然后离开,确保其他进程不会等待过长的时间。