第八章 多级反馈队列(MLFQ)
在操作系统中,调度器的主要目标是决定哪个进程应该在何时运行。然而,我们通常不知道进程的具体行为,比如它会运行多久,或者它是否会频繁地与用户交互(I/O)。
- 问题一:不知道工作长度。 SJF/STCF 等算法能优化周转时间,但它们需要知道工作长度,而我们通常不知道。
- 问题二:响应时间和周转时间的权衡。 轮转法能降低响应时间(交互体验好),但周转时间可能很差。SJF/STCF 能优化周转时间,但可能牺牲了交互性。
- 核心挑战:如何在信息不完备的情况下,设计一个既能优化响应时间,又能优化周转时间的调度器?
MLFQ 的核心思想:从历史中学习,预测未来。
MLFQ 并不是一成不变的,它会根据进程在运行过程中的 表现(反馈)来 动态调整 进程的 优先级。进程表现出什么样的行为,就会被赋予相应的优先级,从而影响其未来的调度。
基础知识梳理:
-
多级队列(Multi-level Queues):
- MLFQ 拥有 多个独立的队列,每个队列拥有一个 不同的优先级。
- 一个进程在任何时刻 只能存在于一个队列 中。
- 高优先级队列中的进程永远优先于低优先级队列中的进程。
-
优先级(Priority):
- MLFQ 的核心是 动态优先级,而不是固定优先级。
- 进程的优先级会随着其行为而改变。
-
调度规则(核心):
- 规则 1:优先级决定运行权。 如果进程 A 的优先级高于进程 B,那么优先运行 A。
- 规则 2:同优先级下的轮转。 如果进程 A 和 B 优先级相同,则对它们进行轮转调度(Round Robin)。
-
进程行为与优先级关联:
- 交互型进程(Interactive Process): 经常需要等待用户输入或其他 I/O 操作。它们通常会 在时间片用完之前主动释放 CPU。MLFQ 希望这类进程有好的响应时间,所以会 保持其高优先级。
- CPU 密集型进程(CPU-bound Process): 运行时间长,长时间占用 CPU。MLFQ 会 降低这类进程的优先级。
知识重点整理:
MLFQ 的发展过程(通过规则的演进):
第一阶段:基本规则与优先级调整(尝试 1)
-
规则 3:新进程进入。 当一个新进程进入系统时,默认给予 最高优先级(放在最上层队列)。
- 目的: 假设所有新进程都可能是短作业,从而近似 SJF/STCF,以优化周转时间。
-
规则 4a:用完时间片。 如果一个进程 用完了它在当前队列分配的时间片(time slice),那么 降低其优先级(移到下一个较低优先级的队列)。
- 目的: 认为长时间占用 CPU 的进程是 CPU 密集型,降低其优先级。
-
规则 4b:主动释放 CPU。 如果一个进程 在其时间片用完之前主动释放了 CPU(例如,等待 I/O),则 保持其优先级不变。
- 目的: 认为主动释放 CPU 的是交互型进程,不应受惩罚,保持其高优先级以提供好的响应时间。
示例分析(此阶段):
- 长工作: 会从高优先级队列逐渐下降到低优先级队列。
- 短工作: 如果在用完时间片前完成,其优先级不会降低,如果确实很快完成,就能近似 SJF。
- 有 I/O 的工作: 由于会主动释放 CPU,优先级保持不变,从而得到更好的响应。
MLFQ 在此阶段存在的问题:
- 饥饿(Starvation): 如果系统中有大量的交互型进程,它们会不断占用 CPU,导致 CPU 密集型进程(即使是优先级较低的)永远得不到运行机会。
- 被“愚弄”(Gaming the Scheduler): 聪明的用户可以编写程序,通过在时间片用完前 伪造 I/O 操作(例如访问一个无用的文件)来欺骗规则 4b,从而 保持高优先级,几乎独占 CPU。
- 进程行为变化问题: 一个进程在不同时间段表现可能不同。如果一个计算密集型进程突然变得像交互型进程,但其优先级已经很低,MLFQ 无法及时识别并提升其优先级。
第二阶段:解决饥饿和进程行为变化(尝试 2)
- 规则 5:优先级提升(Priority Boost)。 周期性地(经过一段时间 S),将 系统中所有进程的优先级都提升到最高级别。
- 目的:
- 解决饥饿: 即使是低优先级的进程,也能周期性地获得高优先级,从而得到运行机会。
- 适应行为变化: 如果一个 CPU 密集型进程变得像交互型进程,在优先级提升时,它会得到正确的对待。
- 目的:
此阶段的问题:
- “巫毒常量”(Voodoo Constant): 时间间隔 S 的设置非常困难。
- S 太大:长工作仍然可能饥饿。
- S 太小:交互型工作得不到足够的 CPU 时间比例。
第三阶段:解决“愚弄”问题(尝试 3)
- 重写规则 4(更好的计时方式): 调度程序 记录一个进程在某一层队列中消耗的总时间(配额)。只要进程用完了这个配额(无论它中间主动放弃 CPU 了多少次),就 将其降到低一级队列。
- 目的: 阻止了通过伪造 I/O 来“愚弄”调度程序的行为。无论进程如何释放 CPU,只要用完配额就会降级。
MLFQ 调优及其他问题(MLFQ 调优及其他问题):
-
配置参数: MLFQ 的配置非常复杂,没有固定的“最优解”,需要根据实际工作负载进行调优。
- 队列数量: 有多少级队列?
- 时间片长度: 每个队列的时间片有多长?(通常高优先级队列时间片短,低优先级队列时间片长)
- 优先级提升间隔 S: 多久提升一次所有进程的优先级?
-
避免“巫毒常量”(Ousterhout 定律): 难以找到完美的 S 值。系统管理员通常通过配置文件设置默认值,希望这些默认值是合理的。
-
MLFQ 的变种:
- 不同队列不同时间片: 高优先级队列时间片短,低优先级队列时间片长。
- 固定优先级留给 OS: 有些系统将最高优先级留给操作系统自身。
- 用户建议(Advice): 允许用户通过
nice命令等工具给出优先级建议,系统可以参考但不强制执行。 - 数学公式: 一些系统(如 FreeBSD)使用数学公式根据进程使用 CPU 的情况计算优先级,并结合使用量衰减(decay-usage)来达到优先级提升的效果。
MLFQ 小结:
- MLFQ 的核心是通过 多级队列 和 反馈信息 来动态调整进程的 优先级。
- 它根据进程的 一贯表现 来区别对待。
- MLFQ 不需要先验知识,通过观察来学习。
- 它可以同时满足 短交互型工作(近似 SJF/STCF,响应时间好)和 长 CPU 密集型工作(公平、稳步前进)。
- 许多现代操作系统(如 BSD、Solaris、Windows NT)都采用了某种形式的 MLFQ。
总结核心思想:
MLFQ 就像一个“观察者”,它给新来的进程一个机会(最高优先级),如果进程证明自己是短作业(很快完成),就很好;如果证明自己是长作业(用完时间片),就降低其优先级。如果进程表现出交互性(频繁释放 CPU),就保持其高优先级。通过周期性的“大赦”(优先级提升),确保所有进程都有机会得到运行,同时防止被“愚弄”。
浙公网安备 33010602011771号