调度
调度简介
当有两个或多个进程通过处于就绪态时,CPU需要选择执行的进程。完成该工作的称为调度程序,调度程序所使用的算法称为调度算法。
进程的行为
I/O活动指进程因等待外部设备(如磁盘)完成工作而阻塞。
计算密集型指进程将大部分cpu时间花在计算上,而I/O密集型指等待I/O上花费了大量的时间。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fuWFyPU1-1630470696910)(./picture/IO与计算交替出现.png)]
I/O密集型的进程会花费大部分时间在等待I/O上,因此CPU利用率不高。为了使CPU保持运转和提高系统的运行效率,应该尽可能的优先处理I/O密集型程序,因为它使用cpu的时间比较短。
cpu突发指CPU从接到命令到开始处理命令锁的所需时间。
进程切换时会将用户态切换成内核态;然后保存当前进程的状态(寄存器、堆栈、程序计数器等)于内核的进程表项中。在许多系统中,还要保存内存的映像;之后,调度程序会选定一个进程;将该进程的内存映像装入MMU中;最后新进程开始运行。此外,进程切换还要使整个内存高速缓存失效,强迫缓存从内存中重新装入两次,进入内核一次,离开内核一次。可见,切换进程的代价高昂。
何时调度
在以下情景中会发生调度。
- 一个进程创建子进程时,需要判断父子进程的调度顺序。两个进程都处于就绪态,因此可以随意选择。
- 进程终止时。当前运行的进程终止后,会选择另外一个就绪的进程,若没有就绪的进程,则系统会执行一个系统提供的空闲。
- I/O中断发生时。I/O中断发生时,意味着IO设备完成了工作,相应的IO进程就应该由阻塞态变成就绪态。是否允许新的就绪进程取决于调度程序
- 进程因为I/O、信号量或其他原因阻塞时。
调度算法分类
硬件时钟会提供50Hz、60Hz或其他频率的周期性中断,在中断发生时需要使用调度决策。根据如何处理时钟中断,有两种调度算法
- 非抢占式
时钟中断到达时,调度程序选择一个进程运行直到其阻塞或该进程放弃CPU。在处理完成时钟中断后,如果没有优先级更高的就绪进程,则会继续被中断的程序。 - 抢占式
时钟中断到达时,会安排一个进程执行一段固定的时间。在该时间结束,若程序仍在工作,则将它挂起,然后选择另外一个就绪的进程。进行抢占式调度,需要在时间段的末尾产生时钟中断,以将控制权从cpu转到调度程序。
调度程序有三种运行的环境:
- 批处理系统
- 交互系统
- 实时系统
批处理会周期性的处理作业,不太会与用户进行交互,因此在这个环境下一般会使用非抢占式算法。
在交互环境下,为了防止一个进程霸占cpu,所以会使用抢占式算法。
在实时系统中,有时候没有必要使用抢占调度算法,因为程序会尽快完成各自的工作然后阻塞。
调度算法指标
调度算法针对不同的系统有着不同的指标。
所有系统
- 公平
同一级别的进程应该给予相同的cpu份额。给同一级别的进程安排更多的cpu时间是不公平的。 - 策略强制执行
保证系统策略强制执行。如当必须运行安全控制进程时,就应该保证能强制运行该进程。 - 平衡
保证系统的所有部分部分尽可能忙碌。例如批处理系统会运行一对作业。所以需要对作业的运行顺序进行安排。一般来说,IO密集型和CPU密集型的进程都应该有,因为这两类进程一个使用更多的cpu,另外一个使用更多的IO。它们的操作互补,所以能提高整个作业的效率。
批处理
- 吞吐量
吞吐量指系统每小时完成的作业数量。 - 周转时间
周转时间指一个作业从开始执行到执行完毕所需要的时间。 - cpu利用率
cpu利用率经常被用来衡量批处理系统,但它不是一个好的度量参数。
交互
- 响应时间
响应时间指发出命令到响应之间的时间。 - 均衡性
用户对某些操作所花费的时间有一个期望值,交互系统应该要尽量的满足该期望值。
实时系统
- 截止时间
每个操作有一个时间的要求,实时系统应该要满足所有(或者大多数)这样的要求。 - 可预测性
批处理系统调度算法
先来先服务
该算法根据进程请求CPU的顺序来安排进程的执行顺序,先请求CPU的进程会先执行。属于非抢占式算法。
该算法会使用一个含就绪进程单一序列,cpu会执行该序列中的第一个进程。若执行的进程因为系统调用而阻塞,则会选取序列中的第一个进程执行。若之前阻塞的进程准备好重新运行时,则会将它放到队列的末尾。
优点:
- 实现简单。一个单链表就能记录所有的就绪进程。执行第一个进程只需删除第一个结点;阻塞或添加进程只需要增加进程到最后一个位置。
缺点:
- IO密集型线程与计算密集交错,则会整个系统的效率低下。因为IO线程只有在计算密集型线程执行完毕后才能执行。
假设有一个运行1s的计算密集型线程,和多个需要1000次磁盘操作的IO密集型线程。每个IO密集型线程在使用完cpu后,不管io操作是否完成,都会等待1s。那么每个IO密集型线程都需要1000s才能完成操作。若有一个调度算法每10ms调度一次,则每个IO密集型线程花费的时间就降为10s。
最短作业优先
若提前知道每个进程的运行时间,则按照运行时间的长短来安排进程运行的顺序,运行时间较短的进程先运行。这会使平均周转时间最短。
假设有4个进程,运行时间分别为a,b,c,d。则平均周转时间为:
KaTeX parse error: No such environment: align at position 8: \begin{̲a̲l̲i̲g̲n̲}̲ avg &= avg_1 +…
平均周转时间中第一个进程的运行时间的权重最高,第二个进程次之。所以当进程按运行时间从小到大排序时,得到的平均周转时间也最短。
最短剩余时间优先
调度程序总是选择剩余运行时间最短的程序运行。该算法会记录有关进程的运行时间,若新加入了一个进程,则会将该进程的运行时间与当前进程的剩余运行时间作对比。如果新进程的运行时间较小,则会挂起当前进程,转而运行新进程。
该算法能使新的短作业得到良好的服务。
交互系统调度算法
轮转调度
该算法为每个进程分配一个时间段,称为时间片,进程会在该时间段中运行。如果时间片结束后进程仍在运行,则会剥夺cpu并分配给另外一个进程。若进程在其时间片结束前阻塞或者结束,则cpu会立刻切换。
轮转调度的实现会用到一张可运行进程列表,当一个进程用完时间片后,会移动到列表的末尾。
关于时间片的选取
进程切换,也称上下文切换,它会保存或装入寄存器值及内存映像、更新各种表格、清除和重新调入高速缓存。这个过程会耗费一定的CPU时间。
若时间片设置的过短,则进程切换所花费的时间相对较高,cpu效率过低。若时间片设置的过长,则后面进程的响应时间会过长。
如果时间片的时间长于平均的CPU突发时间,则多数进程会在时间片结束时发生阻塞,导致进程切换。这时的进程切换不算做损耗,因为它发生在确实需要切换的场景,从而改善了性能。
优先级调度
调度算法会给每个进程分配一个优先级,优先级高的进程优先运行。
为了防止优先级高的进程霸占CPU,导致低优先级的进程处于饥饿状态,可以使用如下的措施:
- 调度程序在每个时间滴答降低当前运行进程的优先级。
- 为每个进程分配的时间片设置一个上限。
优先级既可以动态的设置,也可以静态的设置。
对于I/O密集型线程,它所花费的CPU时间比较少,所以应该尽可能快的调用这类线程,以便启动I/O请求。之后,就能同时执行I/O操作和CPU计算。对I/O密集型线程,有一个方法设置优先级以获得良好的服务:
优 先 级 = 1 / f 优先级=1/f