操作系统中的进程调度算法:最短剩余时间优先(SRTF)详解
1. 知识点描述
最短剩余时间优先(Shortest Remaining Time First, SRTF)是一种进程调度算法。它是短作业优先(SJF)调度算法的可抢占版本。其核心思想是:当一个新进程进入就绪队列时,或当一个正在运行的进程完成其当前CPU突发(burst)时,调度程序总是从就绪队列和当前正在运行的进程中,选择预计剩余执行时间最短的那个进程来分配CPU。如果新到达进程的剩余时间小于当前运行进程的剩余时间,则当前进程会被抢占,CPU将分配给新进程。
SRTF通常被认为是“理论上最优”的抢占式调度算法,因为它能最小化进程的平均等待时间和平均周转时间。然而,在实际操作系统中,它难以实现,因为它依赖于准确预知每个进程的“下一次CPU突发时间”,这通常是未知的。
2. 核心概念与背景知识
在深入SRTF之前,我们需要明确几个概念:
- CPU突发时间:一个进程在获取CPU后,连续执行直到再次进行I/O操作或结束所需要的CPU时间长度。调度算法需要估计这个时间。
- 抢占:操作系统可以在进程运行过程中,强制剥夺其CPU使用权,将CPU分配给另一个更高优先级的进程。
- 可抢占 vs. 不可抢占:SJF是不可抢占的,它只在进程主动放弃CPU(如进行I/O操作或结束)时进行调度决策。SRTF是可抢占的,新进程到达就会触发一次调度比较。
3. 算法原理与执行步骤
让我们通过一个具体的例子,来一步步理解SRTF的工作流程。
假设我们有四个进程,到达时间和CPU突发时间如下:
| 进程 | 到达时间 | CPU突发时间 |
|---|---|---|
| P1 | 0 ms | 8 ms |
| P2 | 1 ms | 4 ms |
| P3 | 2 ms | 9 ms |
| P4 | 3 ms | 5 ms |
调度器的目标是:在任何决策时刻,都让剩余时间最短的进程运行。
第一步:时间0ms
- 就绪队列中只有P1(到达时间0,剩余时间8ms)。
- 调度决策:选择P1运行。
第二步:时间1ms
- P1已运行1ms,其剩余时间变为7ms。
- 新进程P2到达(到达时间1,剩余时间4ms)。
- 比较当前运行进程(P1,剩余7ms)和新进程(P2,剩余4ms)。
- P2的剩余时间(4ms) < P1的剩余时间(7ms)。
- 调度决策:抢占P1,将CPU分配给P2。P1被放回就绪队列。
第三步:时间2ms
- P2已运行1ms(从1ms到2ms),其剩余时间变为3ms。
- 新进程P3到达(到达时间2,剩余时间9ms)。
- 比较当前运行进程(P2,剩余3ms)和新进程(P3,剩余9ms)。
- P2的剩余时间(3ms) < P3的剩余时间(9ms)。
- 调度决策:不抢占。P2继续运行。
第四步:时间3ms
- P2已运行2ms(从1ms到3ms),其剩余时间变为2ms。
- 新进程P4到达(到达时间3,剩余时间5ms)。
- 比较当前运行进程(P2,剩余2ms)和新进程(P4,剩余5ms)。
- P2的剩余时间(2ms) < P4的剩余时间(5ms)。
- 调度决策:不抢占。P2继续运行。
第五步:时间5ms
- P2在时间5ms时运行结束(从1ms运行到5ms,总共运行4ms)。
- 调度决策:现在需要从就绪队列 {P1(剩余7ms), P3(剩余9ms), P4(剩余5ms)} 中选择剩余时间最短的进程。
- P4剩余时间(5ms)最短。
- 将CPU分配给P4。
第六步:时间10ms
- P4在时间10ms时运行结束(从5ms运行到10ms,总共运行5ms)。
- 调度决策:从就绪队列 {P1(剩余7ms), P3(剩余9ms)} 中选择。
- P1剩余时间(7ms) < P3剩余时间(9ms)。
- 将CPU分配给P1。
第七步:时间17ms
- P1在时间17ms时运行结束(从10ms运行到17ms,总共运行8ms)。
- 调度决策:就绪队列中只剩下P3(剩余9ms)。
- 将CPU分配给P3。
第八步:时间26ms
- P3在时间26ms时运行结束(从17ms运行到26ms,总共运行9ms)。
- 所有进程结束。
最终的调度甘特图如下:
时间轴: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
进程: | P1 | P2 | P2 | P2 | P2 | P4 | P4 | P4 | P4 | P4 | P1 | P1 | P1 | P1 | P1 | P1 | P1 | P3 | P3 | P3 | P3 | P3 | P3 | P3 | P3 | P3 |
(抢) (到) (到)
占 达 达
4. 性能指标计算
我们可以计算几个关键性能指标,并与先来先服务(FCFS)进行简单对比,以体现SRTF的优势。
- 周转时间 = 完成时间 - 到达时间
- 等待时间 = 周转时间 - CPU突发时间
- 响应时间 = 首次获得CPU的时间 - 到达时间(对于交互式系统很重要)
| 进程 | 到达时间 | 突发时间 | 完成时间 | 周转时间 | 等待时间 | 响应时间 |
|---|---|---|---|---|---|---|
| P1 | 0 | 8 | 17 | 17 | 9 | 0 (一开始就运行) |
| P2 | 1 | 4 | 5 | 4 | 0 | 0 (一到就被调度) |
| P3 | 2 | 9 | 26 | 24 | 15 | 15 (P3最后才运行) |
| P4 | 3 | 5 | 10 | 7 | 2 | 2 (P4是第3个运行的) |
平均值:
- 平均周转时间 = (17 + 4 + 24 + 7) / 4 = 13.0 ms
- 平均等待时间 = (9 + 0 + 15 + 2) / 4 = 6.5 ms
- 平均响应时间 = (0 + 0 + 15 + 2) / 4 = 4.25 ms
对比思考:如果是FCFS调度(顺序P1->P2->P3->P4),P2、P3、P4都需要等待P1漫长的8ms。平均等待时间和周转时间会远高于SRTF。SRTF通过“让短任务插队”,显著提升了短进程的响应性和系统的整体平均性能。
5. SRTF的优缺点与实现难点
优点:
- 理论最优:在所有基于CPU突发时间的可抢占调度算法中,SRTF能给出最小的平均等待时间和平均周转时间。
- 响应性好:短进程能快速得到CPU,对交互式任务友好。
缺点与难点:
- 无法预知未来:最大的难题是如何准确知道一个进程“下一次的CPU突发时间”。操作系统无法未卜先知。通常采用指数平均预测法,根据该进程历史的CPU和I/O突发时间来估算未来,但这只是预测,并不精确。
- 长进程饥饿:如果系统源源不断地有短进程到达,那么长进程可能会被无限期地推迟,永远得不到CPU,导致“饥饿”(Starvation)。在上面的例子中,P3(长进程)的响应时间就非常长。
- 上下文切换开销大:由于频繁的抢占,进程间的上下文切换会非常频繁,如果进程的平均突发时间很短,这种开销会严重影响系统性能。
6. 实际应用与变体
由于纯粹的SRTF难以实现,实际的操作系统调度器会借鉴其思想,但进行折中:
- 多级反馈队列(MLFQ):这是SRTF思想最成功的实际应用。MLFQ通过动态调整进程的优先级来近似实现SRTF:让短作业(I/O密集型)保持在高优先级队列快速完成,让长作业(CPU密集型)逐渐降到低优先级队列,在无短作业时再运行。这既照顾了短作业的响应性,又避免了长作业饿死。
- Linux的完全公平调度器(CFS):虽然基于虚拟运行时间(vruntime),但其目标也是公平。当一个新进程(vruntime很小)加入时,它会被立即调度,这在一定程度上模拟了“短剩余时间优先”的效果。
总结:最短剩余时间优先(SRTF)是一个重要的调度算法理论模型,它展示了“最短任务优先”这一思想在可抢占环境下的最优形态。理解SRTF有助于深入把握现代操作系统调度器(如MLFQ、CFS)的设计哲学——如何在公平性、吞吐量、响应时间之间取得平衡,以及如何在不预知未来的情况下,智能地猜测进程行为并做出高效调度。