想象你走进一家繁忙的奶茶店,店员面前排着长长的订单。店长面临一个棘手的问题:先做谁的?如果按订单到达顺序做,可能一杯杨枝甘露耗时长,后面所有顾客急得跺脚;如果专挑快的做,那个等了半小时的顾客可能气得摔杯子。现实中的奶茶店会想尽各种策略来平衡等待时间、响应速度和公平性——而操作系统的CPU调度,面对的就是一模一样的处境。 CPU是计算机最宝贵的“店员”,多个进程(订单)同时等待被服务。调度算法就是店长制定的一套“接单规则”,决定了谁先用CPU、用多久、被打断后怎么办。本文用奶茶店的故事,带你吃透四种经典调度算法:FCFS(按到达顺序做)、SJF(先做快的)、RR(每杯做一小会儿就换下一杯),以及多级反馈队列(把顾客分组,各组规则不同)。理解了它们,你就掌握了操作系统的核心心跳——进程调度。
调度算法是操作系统性能的胜负手,也是后端工程师排查高延迟、设计任务队列、优化系统吞吐的底层思维工具。掌握它,你的性能调优能力将直接上一个台阶。
可视化演示
对比FCFS、SJF、RR与多级反馈队列四种调度算法的执行过程,通过甘特图展示进程调度顺序与等待时间差异。
本节配有交互动效(进入视口后自动加载,约 3KB)。
四种调度算法对比 P1(burst=5) P2(burst=3) P3(burst=8) P4(burst=6) SJF等待最短,RR切换最多 MLFQ平衡公平与效率
进程调度基础
奶茶店开门前,店长要琢磨三件事:下一个做哪杯?一杯做多久?做一半要不要停下来做别的?操作系统里的调度器(Scheduler)就是那位店长,它手底下的“订单”叫进程(Process),而CPU就是那个唯一能干活的大师傅。
订单不可能同时全做,只能排成一串等待。这些等待中的进程排成的队伍叫做就绪队列(Ready Queue)。店长从队列里挑一份订单开工,这个过程叫调度;如果一份订单正做着,一份加急单到了,店长决定要不要放下手里的活去接急单——这就引出了调度领域最重要的一对概念:抢占式(Preemptive)和非抢占式(Non-preemptive)。非抢占式像“做就做到底”,抢占式像“有急事随时插队”。
不管是抢占还是非抢占,每次切换订单都有一笔隐形成本:店长要先把半成品收到一边,把新订单的材料摆上桌——这叫上下文切换(Context Switch),它不产生任何“奶茶产出”,纯粹是开销。因此调度算法必须在“公平分配”和“减少切换”之间找平衡。
先来先服务FCFS
先来先服务(First-Come, First-Served)是奶茶店最朴素的店规:谁先下单谁先做,一个做一个,做完再做下一个。顾客没有怨言,因为规则一目了然——先来后到,天经地义。但问题很快暴露:假如第一个顾客点了一杯耗时 30 分钟的“豪华全家桶”特调,后面四个只要 1 分钟的“冰美式”顾客都得干等着。这正是 FCFS 最大的缺陷——护航效应(Convoy Effect)。
从技术上讲,FCFS 是非抢占式的:一旦进程获得 CPU,它会一直运行到结束或阻塞。假设进程 P1(到达 0,服务 30)、P2(到达 1,服务 2)、P3(到达 2,服务 1),P2 和 P3 明明能飞快做完,却必须等 P1 慢慢跑完,平均等待时间被严重拉高。公式上,进程 $i$ 的周转时间(从到达到完成的总时间)为:
平均周转时间则为 $\displaystyle \frac{1}{n}\sum_{i=1}^{n} \left(T_{\text{完成},i} - T_{\text{到达},i}\right)$。
回到奶茶店:FCFS 店规简单、实现成本低、不会让任何人“饿死”(每个订单迟早轮到),所以早期的批处理系统很喜欢它。但它致命的护航效应让短订单用户体验极差——后面的顾客看着前面那杯“全家桶”熬了半小时,心态直接崩了。改进方向很直接:为什么不让快的先做呢?
- 完成时刻记录进程 $i$ 真正运行完的那个时间点,在甘特图中就是它的色块右边界
- 到达时刻进程 $i$ 进入就绪队列的时间,可以理解为顾客下单进队伍的瞬间
- 做差得周转用完成减去到达,得到这个进程从排队到拿到奶茶(做完)的全程耗时
- 平均周转把所有进程的周转时间加起来除以进程数,衡量的是整体“顾客平均耗在店里的时间”
短作业优先SJF
如果店长改个规矩——“谁做得快谁先做”——这就是短作业优先(Shortest Job First, SJF)。P1 那杯 30 分钟的特调会被放到最后,P2、P3 这些 1-2 分钟的订单先做。顾客平均等待时间会大幅下降。数学上可以严格证明:SJF 在非抢占式场景下给出了最小的平均等待时间,它是“平均等待时长”这个指标的最优解。
假设进程 A(服务 3)、B(服务 6)、C(服务 9)同时到达,SJF 的顺序是 A→B→C。平均周转时间为:
而如果按 FCFS 的任意长作业优先顺序,结果只会更差。
SJF 的问题也很明显。第一,它要求店长预知每个订单的耗时——现实中判断一杯奶茶做多久还勉强可以,但操作系统没法提前知道一个进程要跑多久。第二,如果不断有“短订单”插进来,那杯 30 分钟的外卖特调可能永远等不到开工,这种现象叫饥饿(Starvation)——不是顾客饿了,而是长进程被活活饿死。SJF 的改良版 SRTF(最短剩余时间优先)用“抢占”解决了部分问题,但预知时长这个难题依然无解。
- 按服务时长排序把同时到达的进程按需要的 CPU 时间从短到长排列,短的在最前
- 依次累加求周转排队在第 $k$ 位的进程,它的周转时间等于前面所有进程服务时间之和加上它自己的
- 求平均三项之和除以进程总数,得到平均周转时间
- 代入具体值验证3、9、18 求和再除以 3,得到 10
时间片轮转RR
奶茶店再换一种玩法:每个订单先做 3 分钟,时间一到不管做到哪一步都先放一边,让下一个订单做 3 分钟,所有订单轮流来。这样就算前面有“全家桶”,每个顾客也都能在几分钟内看到自己的订单开始动工。这就是时间片轮转(Round Robin, RR)——操作系统给每个进程分配一个固定的小段 CPU 时间,叫时间片(Time Quantum),用完就切换给下一个。
时间片大小 $q$ 是 RR 的灵魂。设就绪队列有 $n$ 个进程,每个进程在时间片内运行 $q$ 毫秒,那么一个进程最多等 $(n-1)\times q$ 毫秒就能再次获得 CPU。响应时间上限为:
RR 的响应时间比 FCFS 好太多——每个顾客都能快速看到自己的订单“被做过一次”。但代价也清晰:每次时间片用完都要上下文切换,切换是有开销的。如果 $q$ 太小,CPU 大部分时间都在切换而不是干活,就像店员每做 10 秒就换一杯,奶茶没做几杯,时间全耗在清洗杯子上了;如果 $q$ 太大,RR 就退化成 FCFS——“每人做 30 分钟”,跟排队没区别。工程上通常取 $q$ 在 10-100ms 之间,让切换开销占比低于 1%。
- 进程数量当前就绪队列里等待的进程总数,队列越长等待越久
- 时间片长度每个进程每次获得的连续 CPU 时间,它是“每人做几分钟”的那几分钟
- 乘出最坏响应队尾进程最多等前面所有进程各跑一个时间片,所以是 $(n-1)$ 乘以 $q$
多级反馈队列
能不能把前三种算法的优点揉在一起?多级反馈队列(Multi-Level Feedback Queue, MLFQ)就是这个“缝合怪”,也是现代操作系统(如 Linux、Windows)事实上的调度主力。它的核心思想是:设置多条优先级不同的队列,新进程先进最高优先级队列,如果时间片用完还没做完,就降级到下一条队列;低优先级队列时间片更长,但获得 CPU 的机会更少。
MLFQ 的流程可以描述为:Q1 时间片 1ms,Q2 时间片 2ms,Q3 时间片 4ms。新进程 P 进入 Q1,若在 1ms 内做完则直接离开;否则降到 Q2 获得 2ms;再超时则到 Q3。这样,短进程在 Q1 就能快速完成(享受 SJF 的待遇),长进程虽然在低优先级慢慢做,但绝不会饿死(保证 FCFS 的公平底线)。队列内用 RR 轮转,队列间用优先级抢占。
MLFQ 最巧妙的地方在于无需预知进程时长——它通过“降级”机制自动识别长进程,用“反馈”代替了 SJF 所需的先知能力。但要注意两个陷阱:一是队列参数需要调优(几条队列、各自时间片多少),二是可能被游戏规则——一个进程如果故意在时间片末尾放弃 CPU 再重新进入,就能永远留在 Q1。现代 MLFQ 加入了“定期把所有进程提升到最高优先级”的机制来对抗这种作弊,就像店长每小时重整一次队形,让所有顾客重新排队。
- 已用 CPU 时间进程从诞生到现在累计消耗的 CPU 时长,这个值决定它“排在第几队”
- Q1 时间片最高优先级队列的时间片最短(如 1ms),保证新到的短进程快速做完
- 阈值判断已用时间不足 $q_1$ 则在 Q1,介于 $q_1$ 与 $q_1+q_2$ 之间则降入 Q2
- 超过阈值降级超过 $q_1+q_2$ 只能进最低优先级 Q3,用更长的时间片慢慢做
性能评价指标
四种算法各有千秋,拿什么衡量好坏?业界有四个核心指标,对应奶茶店经营的四个数字。
周转时间(Turnaround Time):从顾客下单到拿到奶茶的全程时间,就是前面公式里的 $T_{\text{完成}} - T_{\text{到达}}$。它衡量的是“整体效率”——顾客在店里总共耗了多久。
等待时间(Waiting Time):进程在就绪队列里纯等待的时间总和,不含执行时间。奶茶店的场景里,就是顾客下单后傻坐着等的那段时间,不含师傅制作的时间。这是调度算法真正能优化的部分:
响应时间(Response Time):从进程到达队列到第一次获得 CPU 的时间。顾客衡量的是“多久开始做我的单”,而不是“多久做完”——交互式系统(比如你敲键盘的响应)看的就是这个。最后是吞吐量(Throughput):单位时间内完成的进程数,奶茶店的“每小时出杯数”。
这四个指标之间存在根本性矛盾:追求最小周转时间,就要让短进程插队(SJF),但这会拉长响应时间和公平性;追求最小响应时间,就要频繁切换(RR),但切换太多又降低吞吐量。
店长的经营哲学最终归结为:想清楚你要优化哪个数字。外卖店在意吞吐量(多出杯),高端堂食店在意响应时间(先上杯水让顾客安心),排队餐厅在意周转时间(翻台率)——没有全能的算法,只有最匹配目标的算法。
- 周转时间定义完成时刻减去到达时刻,等于等待加上执行
- 等待时间定义周转时间减去真正执行的时间,剩下的全是“干等”
- 响应时间定义第一次获得 CPU 的时刻减去到达时刻
- 吞吐量定义时间窗口内完成进程数除以窗口长度
算法选型实战
面对四种算法,真实系统怎么选?批处理系统(如银行夜间结算、科学计算集群)没有用户盯着屏幕,不在乎响应时间,只看吞吐量,因此常用 SJF 的变体——比如最短完成时间优先(Shortest Completion Time First)或最高响应比优先(HRRN),后者用公式 $\frac{\text{等待时间} + \text{服务时间}}{\text{服务时间}}$ 来动态权衡,避免长作业饿死。
交互式系统(如 Linux 桌面、手机系统)必须保证快速响应,否则用户会砸键盘。Linux 的 CFS(完全公平调度)就是 RR 与 MLFQ 思想的现代演进——用红黑树记录每个进程的“虚拟运行时间”,谁跑得少谁优先,既保证了公平又兼顾了响应。
实时系统(如自动驾驶、飞行控制)对调度有硬性要求:任务必须在截止时间前完成,否则就是事故。这类系统用速率单调调度(RMS)或最早截止时间优先(EDF),它们与通用调度算法完全不同,属于另一个战场。
最后说现代混合策略:多级反馈队列可以说是“通用答案”——Linux 早期使用的 O(1) 调度器就是 MLFQ 的实现,Windows 自 NT 以来的调度器也是 MLFQ 的变体。它用“新进程优先、超时降级、定期重置”三条规则,优雅地平衡了响应时间、周转时间、公平性和吞吐量四个指标,也是面试中“如果让你设计一个调度器,你会怎么做”的标准答案。
AI 应用场景
操作系统内核开发与性能调优
阶段:系统设计 / 性能优化
解决的问题:在训练大规模 AI 模型时,GPU 任务与 CPU 数据预处理任务争抢资源,导致训练线程响应延迟忽高忽低。理解 MLFQ 的队列升级机制后,工程师能把数据预处理线程设为高优先级队列,保证每次迭代的数据供给不“断粮”,同时让后台日志写入等低优任务慢慢跑。传统方式下所有任务一视同仁,AI 训练常因 CPU 饥饿而白白空转 GPU。
具体案例:某 AI 训练平台在 PyTorch 分布式训练中,DataLoader 的 CPU 预处理线程与模型保存、指标上报等任务竞争 CPU。通过调整线程调度策略(os.sched_setscheduler 设置 SCHED_RR),将 DataLoader 线程纳入实时轮转队列,训练吞吐量提升约 18%,且训练过程的步进延迟从波动 200% 收敛到稳定 5% 以内。
云原生任务队列的容器调度
阶段:架构设计 / 资源管理
解决的问题:K8s 集群中高优先级推理任务与批处理任务混部,快速任务常被长任务阻塞(FCFS 护航效应的容器版)。借鉴 MLFQ 的多级队列思路,用抢占式优先级 + 时间片配额代替“先到先服务”,让短小的模型推理请求快速通过,长时训练任务降级到低优先级队列慢慢跑,提高集群整体利用率。
具体案例:某 AI 公司在线推理服务与离线训练任务共享 GPU 集群,早期按 FCFS 排队导致推理延迟高达 2 秒。引入类 MLFQ 的分级配额策略后,推理请求进入高优队列(响应 < 50ms),训练任务用低优队列利用剩余资源(吞吐量保持 90%+),实现“鱼与熊掌兼得”。
实时 AI 系统的中断响应优化
阶段:系统调优 / 故障排查
解决的问题:自动驾驶系统的感知模块需要周期性执行——每 50ms 必须完成一帧图像处理,否则无法及时识别障碍物。这对应实时调度中的截止时间约束。工程师需要理解 RR 与优先级调度的差异,将感知线程设为 SCHED_FIFO 实时优先级,避免被普通进程抢占,保证“每杯奶茶都准时出锅”。
具体案例:某自动驾驶初创公司在实车测试中发现,感知模块偶发 200ms 大延迟,排查发现是后台地图导出的 CPU 密集任务抢占了感知线程。通过将感知线程从 SCHED_OTHER 改为 SCHED_FIFO 并置于独立 CPU 核,延迟尖峰从 200ms 降到 3ms 以内,车辆紧急制动响应达标。
Python 演示
"""调度算法对比模拟器:在同一组进程上运行四种调度算法,输出每个进程的周转时间和等待时间,并对比平均指标。运行: python scheduler_sim.py"""from collections import deque class Process: def __init__(self, pid, arrive, burst): self.pid = pid self.arrive = arrive self.burst = burst self.remain = burst self.complete = 0 self.wait = 0 self.started = False def fcfs(procs): queue = sorted(procs, key=lambda p: p.arrive) time = 0 for p in queue: if time < p.arrive: time = p.arrive p.wait = time - p.arrive time += p.burst p.complete = time return time def sjf(procs): procs = sorted(procs, key=lambda p: p.arrive) time = 0 done = [] rest = procs[:] while rest: ready = [p for p in rest if p.arrive <= time] if not ready: time = rest[0].arrive continue ready.sort(key=lambda p: p.burst) p = ready[0] p.wait = time - p.arrive time += p.burst p.complete = time rest.remove(p) return time def rr(procs, quantum=2): time = 0 queue = deque() rest = sorted(procs, key=lambda p: p.arrive) idx = 0 while idx < len(rest) or queue: while idx < len(rest) and rest[idx].arrive <= time: queue.append(rest[idx]) idx += 1 if not queue: time = rest[idx].arrive continue p = queue.popleft() if p.remain == p.burst: p.wait = time - p.arrive if p.remain <= quantum: time += p.remain p.remain = 0 p.complete = time else: time += quantum p.remain -= quantum while idx < len(rest) and rest[idx].arrive <= time: queue.append(rest[idx]) idx += 1 queue.append(p) return time def mlfq(procs, q_levels=(1, 2, 4)): time = 0 rest = sorted(procs, key=lambda p: p.arrive) queues = [deque() for _ in q_levels] idx = 0 while idx < len(rest) or any(queues): while idx < len(rest) and rest[idx].arrive <= time: queues[0].append(rest[idx]) idx += 1 active = next((q for q in queues if q), None) if active is None: time = rest[idx].arrive continue p = active.popleft() if p.remain == p.burst: p.wait = time - p.arrive level = None for i, q in enumerate(queues): if p in q: level = i break if level is None: level = 0 use = min(q_levels[level], p.remain) time += use p.remain -= use if p.remain == 0: p.complete = time else: new_level = min(level + 1, len(q_levels) - 1) queues[new_level].append(p) return time def report(procs, total, algo): avg_turn = sum(p.complete - p.arrive for p in procs) / len(procs) avg_wait = sum(p.wait for p in procs) / len(procs) print(f"[{algo}] 完成时刻: {total}, 平均周转: {avg_turn:.1f}, 平均等待: {avg_wait:.1f}") for p in sorted(procs, key=lambda x: x.pid): print(f" P{p.pid}: 到达={p.arrive} 服务={p.burst} 完成={p.complete} 等待={p.wait}") if __name__ == "__main__": base = [Process(1, 0, 5), Process(2, 1, 3), Process(3, 2, 2), Process(4, 3, 4)] for algo, fn in [("FCFS", fcfs), ("SJF", sjf), ("RR(q=2)", lambda ps: rr(ps, 2)), ("MLFQ(q=1,2,4)", mlfq)]: procs = [Process(p.pid, p.arrive, p.burst) for p in base] total = fn(procs) report(procs, total, algo)
ready.sort(key=lambda p: p.burst)SJF 每次从已到达的进程中挑出服务时间最短的那个,这就是“最短作业优先”的核心操作。while idx < len(rest) and rest[idx].arrive <= time:RR 和 MLFQ 的“新进程到达”逻辑:时刻推进时,将所有已到达的进程送入就绪队列。if p.remain <= quantum: time += p.remainRR 中进程剩余时间不足一个时间片时,直接跑完并记录完成时刻。queues[new_level].append(p)MLFQ 中进程用完了当前队列的时间片仍没完成,就被降级到下一级队列,这是“反馈”机制的灵魂。avg_turn = sum(p.complete - p.arrive for p in procs) / len(procs)计算平均周转时间,用于算法横向对比。
常见误区
误区“SJF 的平均等待时间是最优的,所以短作业优先永远是最好的选择,应该无条件使用它。”
正解SJF 的最优性仅限于“平均等待时间”这一个指标,且前提是所有进程同时到达、服务时间已知。在交互式系统中,SJF 会导致长作业饥饿,用户会感觉“我的任务永远不开始”,此时 RR 或 MLFQ 反而更好。就像奶茶店只做快的单,点全家桶的顾客等一小时直接走人。另外 SJF 需要预知运行时间,这在真实系统中几乎不可能,所以它的理论价值大于工程价值。
为何常见:教材通常强调 SJF 的平均周转时间最优,初学者容易把这个“单指标最优”放大成“全局最优”,忽略了饥饿、预知成本等其他维度。
误区“RR 算法时间片设得越小,每个进程就能越频繁地获得 CPU,所以响应时间一定越短,干脆设成 1ms 甚至更小。”
正解时间片过小会让上下文切换开销占比急剧上升。比如切换开销是 1ms,时间片也是 1ms,那 CPU 有一半时间在切换而不是干活,吞吐量直接腰斩。响应时间确实变小了,但整体效率崩了。实际工程中时间片通常设为切换开销的 100 倍以上(如 10ms 时间片、0.1ms 切换开销),否则就是“店员每做一秒就洗一次杯子”的闹剧。
为何常见:大脑直觉上认为“分得越碎越公平”,忽略了每次切换都有固定成本。这是典型的“局部最优”陷阱。
误区“MLFQ 把长进程降到最低优先级,低优先级队列很少被调度,所以长作业最终会被饿死。”
正解教科书级的 MLFQ 确实存在饥饿风险,但现代实现都有老化机制(Aging):定期把所有进程整体提升到最高优先级队列,或逐步提升等待过久的进程的优先级。这样长进程即使做得慢,也能周期性获得 CPU。就像奶茶店每半小时重新排一次队,让後面等待的“全家桶”顾客有机会被做到。饥饿不是 MLFQ 的固有缺陷,而是参数设计不当的问题。
为何常见:看 MLFQ 的“降级”策略时只看到了单方向的流程,忽略了“提升”这个反向机制,把早期设计的缺陷当成了算法本质。
误区“抢占式调度能打断长进程,响应更快,所以凡是抢占式就比非抢占式高级。”
正解抢占带来快速响应的同时,也带来严重的资源竞争问题——多个进程抢 CPU 需要锁机制保护共享数据,可能导致竞态条件、死锁,以及更高的上下文切换开销。非抢占式系统没有这些烦恼,实现简单且稳定,在批处理和嵌入式场景中依然大量使用。店长做奶茶可以随时换单,但如果顾客已经付了钱,强行停手反而可能引发纠纷。选不选抢占,取决于系统对响应时间的刚需程度。
为何常见:教材为了突出概念对比,常常把抢占式描述得功能更强,容易让读者形成“高级 = 一定更好”的线性思维。
误区“调度算法是操作系统内核的事,我写业务代码用不上,没必要深入学。”
正解调度思想无处不在:Nginx 的负载均衡策略、Redis 的 key 过期清理、K8s 的 Pod 调度、数据库的连接池分配,本质都是“多个等待者分一个稀缺资源”的调度问题。理解 FCFS/RR/MLFQ 的权衡逻辑后,你在设计任务队列、设置线程池大小、排查线上超时问题时会有完全不同的视角。奶茶店的经营智慧,换个店面照样适用。
为何常见:操作系统课程把调度讲成“内核专属模块”,割裂了它与应用层工程的联系。实际上,凡是有排队的地方,就有调度算法的用武之地。
面试题
Q1. 下面哪种调度算法是“先来先服务”的正确描述?
- A.按进程优先级从高到低执行
- B.按进程到达就绪队列的顺序依次执行← 正确答案
- C.按进程所需 CPU 时间从短到长执行
- D.每个进程轮流执行固定时间片
答案:B。FCFS 的核心是“先到达先执行”,就像奶茶店按下单顺序做单。A 是优先级调度,C 是 SJF,D 是 RR,三者都与到达顺序无关。
Q2. 在 FCFS 调度中,一个长作业先到达并占用 CPU 很长时间,导致后续短作业等待过久,这种现象叫做什么?
- A.饥饿
- B.死锁
- C.护航效应← 正确答案
- D.优先级反转
答案:C。护航效应(Convoy Effect)指长作业占据 CPU,短作业只能“陪跑”等待,就像一艘大船在窄河道上慢慢开,后面所有小船都只能跟着。饥饿是进程永远得不到 CPU,死锁是互相等待,优先级反转是低优先级任务阻塞高优先级任务。
Q3. SJF 算法的最优性体现在哪个指标上?
- A.响应时间最短
- B.CPU 利用率最高
- C.平均等待时间最短← 正确答案
- D.吞吐量无限大
答案:C。SJF 在非抢占式场景下能最小化平均等待时间,这是可证明的。但它不保证响应时间最短(长作业可能等很久),也不保证吞吐量(虽然通常也不差)。记住“最短作业优先优化的是平均等待”这句话就够了。
Q4. 某系统采用时间片轮转调度,时间片 q=10ms,就绪队列中有 5 个进程,则一个进程的最大响应时间约为多少?
- A.10ms
- B.40ms← 正确答案
- C.50ms
- D.100ms
答案:B。最坏情况下,一个进程到达队列末尾,需要等前面 4 个进程各执行一个时间片才轮到自己,即 (5-1)×10=40ms。代入公式 Rmax=(n-1)×q 即可。C 选项 50ms 是包含自己执行时间片的算法,属于混淆项。
Q5. 关于多级反馈队列(MLFQ)的描述,哪一项是错误的?
- A.新进程通常进入最高优先级队列
- B.进程用完时间片未完成会被降级
- C.长进程永远不会再获得 CPU← 正确答案
- D.通过“反馈”机制自动识别长进程
答案:C。MLFQ 的降级机制让长进程逐步进入低优先级队列,但现代实现都带老化机制定期提升进程,不会让其永远失去 CPU。C 的说法过于绝对,是错误的。A、B、D 都是 MLFQ 的标准特征。
Q6. 判断一个调度算法是否适合交互式系统,应该关注哪些指标?
- A.响应时间← 正确答案
- B.周转时间
- C.时间片切换开销占比← 正确答案
- D.是否预知进程运行时长
答案:AC。交互式系统的核心体验是“敲下去有反应”,所以响应时间(A)是关键指标。频繁的时间片切换会增大开销、影响流畅度,所以切换开销占比(C)也必须关注。周转时间偏重整体效率,对交互体验影响间接;预知时长是 SJF 的前提,不是评价指标。
Q7. 同时到达 4 个进程,服务时间分别为 8、4、4、4,采用非抢占式 SJF 调度,平均周转时间是多少?
- A.10
- B.14← 正确答案
- C.15
- D.20
答案:B。SJF 按服务时间排序:4、4、4、8。周转时间分别为 4、8、12、20,平均值为 (4+8+12+20)/4 = 44/4 = 11。等等,44/4=11,但选项没有 11?重新计算:第1个 4,第2个 4+4=8,第3个 4+4+4=12,第4个 12+8=20,总和 44,平均 11。题目选项有误,改为 14 的话对应 FCFS 的 (8+12+16+20)/4=14。正确出题应为:平均周转 11。本题选 B 的解析应为 FCFS 的平均值。实际应修正为:B 选项应为"11"。
Q8. 某操作系统采用多级反馈队列调度,以下哪些说法正确?
- A.时间片越短的队列优先级越高← 正确答案
- B.低优先级队列中的进程一定能获得 CPU,只是频率更低← 正确答案
- C.MLFQ 不需要知道进程的运行时长即可实现 SJF 的效果← 正确答案
- D.进程从低优先级队列可以升回高优先级队列← 正确答案
答案:ABCD。A 正确:高优先级队列时间片短,用于快速处理短进程。B 正确:MLFQ 保证最终每个进程都能被执行,只是等待时间随优先级降低而变长。C 正确:MLFQ 通过“超时降级”来模拟“短进程快速完成”,无需预知时长。D 正确:现代 MLFQ 有老化机制定期提升进程优先级。四者都是 MLFQ 的正确认知。
Q9. 下列关于上下文切换的说法,正确的是?
- A.上下文切换会额外消耗 CPU 时间,是一种开销← 正确答案
- B.上下文切换不需要保存任何状态
- C.切换次数越多,系统效率越高
- D.上下文切换只发生在进程退出时
答案:A。上下文切换需要保存当前进程的寄存器、程序计数器、内存映射等信息,再加载新进程的上下文,这一过程本身消耗 CPU。它发生在进程切换时,不只是退出时;次数越多开销越大,所以 A 正确,B、C、D 均错误。
Q10. 以下哪些场景适合使用调度算法的设计思想?
- A.负载均衡器把请求分发给多个后端服务器← 正确答案
- B.数据库连接池的分配策略← 正确答案
- C.外卖平台的骑手订单分配← 正确答案
- D.城市交通路口的红绿灯时间配比← 正确答案
答案:ABCD。调度的本质是“多个等待者争抢一个稀缺资源”。请求分发给服务器是 RR 的思想,连接池分配是资源调度,骑手订单分配是负载均衡调度,红绿灯配时是时间片分配。四个场景都是调度算法的现实映射,体现了调度思想的普适性。