调度
1. 何时使用调度?
- 在创建一个新进程后,需要决定是运行父进程还是运行子进程。
- 在一个进程退出时必须做出调度决策,以选择另外某个进程。
- 当一个进程阻塞在I/O和信号量上或由于其他原因阻塞时,必须选择另一个进程运行。
- 在一个I/O中断发生时,必须做出调度决策。如果中断来自I/O设备,而该设备现在完成了工作,某些被阻塞的等待该I/O的进程就成为可运行的就绪进程了,是否让新就绪的进程运行,这取决于调度程序的决定,或者让中断发生时运行的进程继续运行,或者应该让某个其他进程运行。
2. 调度的分类
- 批处理:适用于用户不会在终端旁等待一个短请求的快捷响应(如商业领域,计算薪水,存货等)
- 交互式:避免一个进程霸占CPU拒绝为其他进程服务(如服务器),对于这类调度,抢占是必要的
- 实时:抢占有时是不需要的,因为进程了解它们可能会长时间得不到运行,所以通常很快地完成各自的工作并阻塞。与交互系统的差别是,实时系统只运行那些用来推进现有应用的程序,而交互式系统是通用的,它可以运行任意的非协作甚至是有恶意的程序
3. 调度要实现的目标
所有系统
- 公平:相似的进程应得到相似的服务。不应该给予一个进程较其他等价进程更多的CPU时间。
- 策略强制执行:看到所宣布的策略执行。例如,如果策略为只要需要就必须运行安全控制进程,那么即便这意味着要将其他进程推迟30秒,也要保证能够强制执行该策略
- 忙碌:保持CPU和I/O设备始终能够运行。例如对进程进行组合,CPU密集型和I/O密集型,交替运行。
- CPU密集型(计算密集型):进程花费了绝大多数时间在计算上
- I/O密集型:进程在等待I/O上花费了大量时间。
批处理系统
- 吞吐量:每小时最大作业数。
- 周转时间:从提交作业到作业终止的平均时间(即期望得到输出与实际得到输出的平均等待时间)。
- CPU利用率:保持CPU始终忙碌。
交互式系统
- 响应时间:希望快速响应请求,即有最小的发出命令到得到响应的时间。例如,请求打开文件应该优于后台处理电子邮件的进程。
- 均衡性:满足用户的期望。
实时系统
- 满足截止时间:避免丢失数据。
- 可预测性:在多媒体系统中避免品质降低。
4. 批处理系统中的调度
先来先服务(First Come First Service)
用队列来维护所有进程,当一个进程请求CPU时就被放在队列尾部,CPU处理完当前任务就取出队列头的任务处理。该方式为非抢占式的,即当前任务处理完或者阻塞时,才会轮到下一个任务。如果一个任务被阻塞,它会被重新放到队尾,就像一个新作业一样。如果两个任务同时请求CPU,那哪个任务先被放到队尾是不确定的。优点:
- 易于理解,实现简单
缺点:
- 假设有一个一次运行1秒钟的计算密集型进程和很少使用CPU但是每个都要进行1000次磁盘读操作的I/O密集型进程。计算密集进程运行1秒钟,然后读一个磁盘块。所有的I/O进程开始运行并读磁盘。当该计算密集进程获得其磁盘块时,它运行下一个1秒钟,紧跟随着的是所有I/O进程。
这样做的结果是,每个I/O进程在每秒钟内读到一个磁盘块,要花费1000秒钟。
最短作业优先(Shortest Job First)
在同等重要的作业中,选择需要运行时间最短的作业。该方法只适用于运行时间可以预知的情况。在所有作业重要程度相等且可以同时运行的情况下,最短作业优先算法有最小的平均周转时间。最短剩余时间优先(Shortest Remaining Time First)
最短作业优先方法的抢占式版本。同样该方法也只适用于运行时间已知的情况。这种方法可以使新的端作业获得良好的服务。
5. 交互式系统中的调度
轮转调度(Round Robin)
每个进程被分配一个时间段,称为时间片,即允许该进程在该时间段中运行。如果在时间片结束时该进程还在运行,则将剥夺CPU并分配给另一个进程。如果该进程在时间片结束前阻塞或结束,则CPU立即进行切换。所以,轮转调度是抢占式的。例如,时间片时间为2ms,进程A需要3ms才能完成,进程B只需要1ms完成。A先请求CPU,CPU先运行进程A,运行2ms,尽管A并为完成,但是时间片已经到达了,CPU转而运行进程B。只运行1ms,虽然时间片未到达但是B已经运行完毕,CPU就继续切换到A运行。
在该方法中时间片的选择很重要,因为进程的上下文切换需要时间如果时间片太小,则会有很多上下文切换浪费性能。如果太大,那么在时间片完成前就会产生很多阻塞,也会延长短的交互请求的响应时间。优先级调度
对轮转调度的改进,可以为每个进程设置优先级。允许优先级高的进程先运行,而为了防止高优先级进程无休止地运行下去,调度程序可以再每个时钟中断降低当前进程的优先级。可以按照优先级将进程分组(如按优先级1,2,3,4分为四组)每组有一个队列,当高优先级有可运行进程时,不会运行低优先级队列中的进程。如果不对优先级进行调整,低优先级队列有可能产生饥饿。
多级队列
对优先级调度进行改进,当一个进程运行完指定时间且未结束时,被自动下调一个优先级,并放入下一层优先级进程队列的结尾。同时,优先级最高的队列中的程序每次运行1个时间片,次高的每次运行两个时间片,然后是4,8,16…以此类推。这样,可以防止CPU密集型进程的频繁切换,以及长时间片进程影响响应时间。
如图所示,假如进程A需要运行5ms,进程B需要运行2ms,C进程要运行1ms。如果A先开始运行,当它运行时,一开始A的优先级为0(即最高优先级),此时A的时间片为2^0 为1ms。并且在此期间进程B也请求CPU,B的优先级期初也为0,被放置在优先级为0的队尾。
当A运行1ms时,A被打断并下调优先级为1,被放入优先级为1的队列中。此时运行B,时间片也为1ms。并且C也请求CPU,并被放入优先级为0的队尾。当B运行1ms时和A一样,也被打断并放入优先级为1的队尾。此时优先级为0的队列中有C,而优先级为1的队列中有A,B。C的优先级比A,B都高。所以此时运行C,因为C 在1ms内可以运行完毕。并且没有更多新任务到达。所以当C运行完毕后优先级为0的队列就被清空。没有任务的优先级高于A。
此时转而运行A,A这时的时间片应为2^1为2ms,A运行2ms,并未结束,被打断并放入优先级为2的队列。此时B的优先级为1,A的优先级为2,该运行B。B的时间片也为2ms,但是运行1ms时B就可以结束了。所以这时,检查队列0和1中没有任务,就可以运行队列2中的队头A,时间片为 2^2为4ms,A只运行2ms,完成任务。此时所有任务都可以完成了。
缺点:
- 该方法存在的问题为,如果一个任务需要长时间才能完成,会被放到比较低的优先级,而如果此时一直有新任务到来,那么这个长任务会饥饿,直到那些短任务都执行完,它才会执行。
保证调度
向用户作出明确的性能保证,然后实现。若用户工作时有n个用户登录,则用户将获得CPU处理能力的1/n。类似地,在一个有n个进程运行的单用户系统中,若所有进程都等价,则每个进程获得1/n的CPU时间。系统必须跟踪各个进程自创建以来已使用了多少CPU时间。然后它计算各个进程应获得的CPU时间,即自创建以来时间除以n,然后求出真正获得的CPU时间和应获得的CPU时间。然后运行比率最低的进程,直到它超过其他进程。彩票调度
每个进程在一定时间内可以得到一张“彩票”(CPU时间),重要进程一次可以得到多个彩票。规则是:拥有彩票F份额的进程大约得到系统资源的F份额。还可以让进程交换他们的彩票。例如,客户机和服务机,客户机得到彩票,并且请求服务机,因为客户机需要的CPU时间很短,所以可以将彩票中剩下的时间交换给服务机。
公平分享调度
关注进程所有者,而不是进程本身。每个用户分配到CPU时间的一部分。缺点是每个用户想要执行的进程多少不一样,可能浪费性能。最高响应比优先
通过W+CC这个公式求得每个任务最高响应比,结果最大的优先执行- W代表该任务已经等待的时间
- C代表该任务想要完成需要的时间
6. 实时系统中的调度
实时系统是一种时间起着主导作用的系统。首先要理解截止时间,即必须在此时间之前作出应答。在实时系统中,迟到的应答可能比没有应答更为糟糕。
实时系统可以分为硬实时和软实时,前者的含义是必须满足绝对的截止时间,后者的含义是虽然不希望偶尔错失截止时间,但是可以容忍。
实时系统中的事件可以分类为周期性(以规则的时间间隔发生)事件或非周期性(发生时间不可预知)事件。一个系统可能要响应多个周期性事件流。根据每个时间需要处理时间长短,系统可能无法处理完每个事件。例如如果有m个周期事件,事件i以周期Pi发生,并需要Ci秒CPU处理一个时间,那么可以处理负载的条件是:
7. 策略和机制
以上调度方法是基于所有进程分属不同用户,并且进程间相互竞争CPU的假设。但是如果一个进程有许多子进程,并在其控制下运行。例如,一个数据库管理系统可能有许多子进程,每一个子进程可能处理不同的请求,或每一个子进程实现不同的功能。主进程可以掌握哪一个子进程最重要,哪一个不重要。
为了做出最优选择,解决办法是将调度机制与调度策略分离,即将调度算法以某种形式参数化,而参数可以由用户进程填写。假设内核使用优先级调度算法,但提供一条可供进程设置优先级的系统调用。这样尽管父进程不参与调度,但可以控制子进程的调度。在这里调度机制位于内核,而调度策略由用户决定。
8. 线程调度
现成的调度取决于使用的是用户级线程还是内核级线程
用户级线程:选取一个进程A,给予时间片。A中的线程调度决定哪个线程运行,假如为A1,。由于这里线程间不存在时钟中断,所以该线程可以一直运行。如果该线程用完了进程的全部时间片,内核选择另一个进程运行。当再一次运行进程A时,A1也会接着运行,直到其完成工作。(进程内部问题不会影响其他进程)
内核级线程:内核选择一个特定的线程运行,不用考虑线程属于哪个进程。
用户级线程切换需要少量机器指令,而内核级需要完整的上下文切换,修改内存映像,使高速缓存失效。另一方面,内核级线程,一旦线程阻塞在I/O上不需要像在用户级线程中那样将整个进程挂起。从进程A的一个线程切换到进程B的一个线程,代价要远高于切换到切成A的另一个线程。所以内核在切换时,会倾向于后者。
用户级线程可以使用定制的线程调度程序。假如一个分派线程给两个工作线程分派任务,其中一个工作线程刚刚被阻塞,所以这时分派线程应该接着运行,并启动另一个未堵塞的工作线程。但在内核级线程中,内核不了解每个线程的作用。
当工作者线程从磁盘读取 Web 页时,它就会被阻塞。如果使用用户级线程,该动作将阻塞整个进程,而
破坏多线程的价值。这就是使用内核线程的原因:某些线程的阻塞不会影响到其他线程
参考书目:现代操作系统第三版,分布式系统原理与范型第二版