
操作系统这门课里进程管理章节有一类问题只要你动手写过模拟器就绕不开一堆进程排队等 CPU谁先上、谁等多久、什么时候切走全由调度算法说了算。FCFS、RR、SPNSJF、SRT、HRRN 这几个名字几乎出现在每一本操作系统教材的进程管理部分也是期末、考研复试、后台开发面试的高频考点。但很多人背完定义就翻篇了真拿一组到达时间和服务时间让他手算周转时间或者写个模拟器把五套策略跑一遍做对比立刻就卡住。我做课程设计和后来写监控工具的时候前后把这几套算法实现过好几轮中间踩的坑不少比如时间片设成 1 和设成 4同一组数据算出来的平均周转时间能差出一个身位再比如 SRT 的抢占边界到底卡在哪一刻不同教材的约定并不完全一致。这篇就把这些算法从为什么这么设计到怎么手算再到怎么用代码跑通验证完整走一遍最后把常见的坑和易混点整理成速查表。不管你是在准备考试、做课程设计还是单纯想搞明白调度器脑子里在算什么都能直接拿去用。1. 调度算法在解决什么问题先把评价指标立起来调度算法这个词听起来抽象本质其实很朴素CPU 只有一个或者核心数有限但就绪队列里随时可能堆着几十上百个任务谁先用、用多久、用完怎么排这套规则就是调度算法。评价一套规则好不好光说公平没用得有一把尺子。这把尺子就是下面几个指标后面所有推演都围绕它们展开。1.1 CPU 和 IO 的节奏差异决定了必须有人插队先想一个场景你写了个程序里面有个循环读文件的操作每次读盘要等几十毫秒CPU 在这期间其实啥也没干。如果这时候按谁先来谁先跑的顺序死等后面一个只需要 1 毫秒就能算完的任务就只能干等着用户体验直接崩掉。这就是调度必须存在的根本原因——不同任务的性格差别太大。业内一般把任务分成两类一类是CPU 密集型比如视频转码、科学计算它占着 CPU 就不撒手一跑就是几百毫秒甚至几秒另一类是IO 密集型比如网络请求处理、键盘输入响应它跑几毫秒就要去等一次 IOCPU 占用时间极短但交互频繁。调度算法的核心矛盾就在于如果偏向 CPU 密集型长任务能一口气跑完上下文切换少吞吐量高如果偏向 IO 密集型短任务能快速得到响应交互体验好但切换开销上去了。所以你在看任何一个调度算法时都要先问一句它优化的是哪一类任务FCFS 优化的是简单、切换少RR 优化的是响应快SJF 优化的是平均周转时间短。没有哪个算法能同时把这两类任务都照顾到极致所谓选型本质上是在这两类需求之间做取舍。注意教学里常说的CPU 利用率指标现在的机器上一般不是瓶颈真实的调度器更多在权衡延迟和吞吐。别把教材里的理想模型直接套到生产环境上。1.2 五个指标周转、带权周转、等待、响应、吞吐看调度算法效果最常用的几个指标必须先搞清楚不然后面算出来的数字没法解读。周转时间Turnaround Time指的是从进程提交到达到它完成的时间公式是周转时间 完成时间 - 到达时间提交时间带权周转时间Weighted Turnaround Time是周转时间除以实际服务时间它衡量的是这个进程被拖了多久——服务时间越短的任务带权周转时间越容易被拉高。一个只需 1 毫秒的任务如果等了 8 毫秒才跑完带权周转就是 8看起来非常难看。公式是带权周转时间 周转时间 / 服务时间等待时间是进程在就绪队列里干等的时间等于周转时间减去服务时间假设它没有额外的 IO 等待。响应时间是用户视角的指标指从提交到第一次得到 CPU 的时间交互式系统最关心它。吞吐量则是单位时间内完成的任务数量。这里有个容易被忽略的点平均周转时间最小不代表带权周转时间也最小。后面你会看到SJF 系列算法能把平均周转时间压得很低但代价是长作业被反复插队它的带权周转时间可能很难看。考试里出题人特别喜欢在这两个指标上做文章手动算的时候千万别只算一个。2. 五种算法的设计思路拆解与取舍逻辑把指标立好之后再逐个看这五种算法的设计动机。我习惯把它们的演进逻辑串成一条线从最省事的 FCFS 出发发现响应太差于是有了 RR发现平均周转太长于是有了 SJFSJF 有个硬伤预测未来 长作业饿死于是有了 SRT 抢占版和 HRRN 折中版。这样串起来记比孤立地背五个定义牢得多。2.1 FCFS队列的天然顺序简单但容易被长作业拖垮FCFSFirst Come First Served先来先服务的思想就是排队买票谁先到谁先服务非抢占式。实现上只需要一个先进先出队列新进程来了放队尾CPU 空闲了从队头取一个跑完再取下一个。它的优点很突出实现简单没有切换开销对长作业公平。但它的问题同样突出护航效应Convoy Effect。假设队头是一个跑了 100 毫秒的长作业后面紧跟着一堆只需 1 毫秒的短作业这些短作业就只能全部等 100 毫秒平均等待时间被一个长作业绑架了。用数学语言说FCFS 的平均等待时间对长作业的排列顺序高度敏感只要一个超长任务卡在前面后面全遭殃。我实测过一组数据四个进程到达时间分别是 0、2、4、5服务时间是 7、4、1、4。FCFS 跑下来平均周转时间是 8.75。而同样这组数据把短作业提前平均周转立刻降到 8.0 附近。也就是说仅仅调整一下执行顺序平均性能就能差出将近 10%。FCFS 只有在任务长度比较均匀的场景下才不至于太难看。2.2 RR用固定时间片换响应时间公平是买来的RRRound Robin时间片轮转的出发点很直接既然 FCFS 会让短任务被长任务堵住那就强制规定每个进程最多连续跑一个时间片Time Slice跑完就丢回队尾换下一个。这是一个抢占式算法严格说是被时钟中断抢占交互式系统的经典选择。RR 里最重要的参数就是时间片大小 q。q 太大退化成 FCFS响应变差q 太小上下文切换开销占比飙升CPU 光在切进程就把时间耗光了。教材里给的参考值是略大于一次上下文切换时间实践中一般取 10 到 100 毫秒这个区间具体要看切换代价和场景。拿前面那组数据我用 q1 手算 RR平均周转是 9.75比 FCFS 的 8.75 还差。这不是算错了——RR 为了照顾响应时间牺牲了平均周转。当所有任务长度都接近时RR 的表现也不差但当任务长度差异极大时RR 会让短任务反复排队平均周转反而上升。所以 RR 的价值不在周转指标而在每个任务最多等多久就能上一次 CPU也就是响应时间可控。用 q1 和 q4 分别跑同一组数据你会看到平均周转从 9.75 降到 8.5但短任务 P3 的带权周转从 3 涨到 5——时间片越大越接近 FCFS 的脾气。提示RR 的队列顺序有约定问题。如果时间片用完的瞬间正好有新进程到达是先放回当前进程还是先放新进程不同实现结果不同。考试时按题目约定来写代码时一定要在注释里写清楚。2.3 SPN/SJF把平均周转做到理论最优代价是预测未来SPNShortest Process Next最短进程优先也叫 SJFShortest Job First短作业优先思路是每次从就绪队列里挑服务时间最短的那个执行非抢占式。它的理论地位很高——可以证明在所有非抢占式调度里SJF 的平均等待时间是最小的前提是任务长度已知。推导逻辑其实不复杂假设队列里有两个任务服务时间分别是 a 和 b且 a b。如果先跑 a 再跑 b两个任务的等待时间之和是 0 a a反过来先跑 b 再跑 a等待时间之和是 0 b b。因为 a b先短后长更优。把这个结论推广到 n 个任务就得到按服务时间递增顺序执行能让平均等待时间最小。SJF 的致命问题是两个。第一服务时间在实际系统里没法预先知道只能靠历史数据估算比如用指数平均法预测下一轮 CPU 突发长度估错了效果就打折。第二长作业可能被一直插队永远得不到执行这就是饥饿Starvation。经典的极端例子是一个长作业刚准备跑每来一个短作业都插到它前面只要短作业源源不断长作业就永远等下去。这个问题的解法之一就是后面要讲的 HRRN。2.4 SRT给 SJF 装上抢占开关短作业一到就切SRTShortest Remaining Time最短剩余时间优先是 SJF 的抢占版本。规则变成每当有新进程到达就比较它的服务时间和当前进程的剩余时间如果新来的更短立刻切过去。它同样是非抢占式 SJF 的升级版。SRT 的优势在短作业密集的场景里非常明显。还是那组数据SRT 跑出平均周转 7.5是五种算法里最低的。原因在于它不等当前进程跑完只要来了更短的就抢占把短作业的等待时间压到极限。代价是什么呢长作业被反复打断恢复运行的时间点不断后移。在上面的例子里服务时间 7 的 P1 被一路插队完成时间被拖到 18带权周转 2.571而服务时间 1 的 P3 周转只有 1。长作业的用户体验会很糟。SRT 的实现还涉及一个边界问题当新到达进程的服务时间恰好等于当前进程的剩余时间时要不要抢占大多数教材的约定是不抢占按 FCFS 继续因为抢占本身有开销没有收益就没必要切。这个细节在考试里经常埋雷写模拟器的时候也要在代码里显式处理。2.5 HRRN响应比把等待时间折成竞争力HRRNHighest Response Ratio Next高响应比优先是专门用来治 SJF 饥饿病的非抢占式算法。它给每个就绪进程算一个响应比响应比 R (等待时间 服务时间) / 服务时间 1 等待时间 / 服务时间每次调度时选响应比最高的那个。这个公式的精妙之处在于它同时考虑了两个因素服务时间越短R 越大保留了 SJF 的优点等待时间越长R 也越大等待足够久再长的作业也能把 R 顶上去。这就保证了任何进程只要等得够久最终一定会被选中饥饿问题被从机制上解决了。用一个对比算例来说明。进程 P1、P2、P3、P4 到达时间 0、2、4、6服务时间 6、5、2、3。SJF 跑出来的平均周转是 7.25HRRN 是 7.75HRRN 略差一点点。但看单体指标服务时间 5 的 P2 在 SJF 下要等到最后周转 14在 HRRN 下因为它在 t8 时的响应比已经涨到 2.2反超了 P4 的 1.667被提前执行周转降到 11。HRRN 用整体平均性能上的一点让步换来了单个进程的公平性这就是它的设计哲学。3. 同一组数据五种算法手算一遍光讲思路容易飘必须拿数据落地。我选一组长短悬殊的经典数据因为它能把五种算法的差异全部放大出来。手算过程我会写全你跟着推一遍基本就再也忘不掉了。3.1 测试数据与甘特图的画法约定数据如下单位统一用时间单位| 进程 | 到达时间 | 服务时间 | | P1 | 0 | 7 | | P2 | 2 | 4 | | P3 | 4 | 1 | | P4 | 5 | 4 |甘特图我用文本形式表示格式是进程[开始,结束]按时间顺序排列。约定几点到达时刻正好有进程到达的该进程视为此刻已就绪服务时间相同的按到达时间先后决定抢占式算法里新到达进程的服务时间等于当前剩余时间时不抢占。这些约定在考试和写代码时都要提前定死不然同一组数据能算出两个答案。3.2 逐个算法推演FCFS按到达顺序 P1、P2、P3、P4 依次执行。P1[0,7] - P2[7,11] - P3[11,12] - P4[12,16]周转时间P17-07P211-29P312-48P416-511。平均周转 (79811)/4 8.75。带权周转1、2.25、8、2.75平均 3.5。P3 这个只需 1 个单位的短作业被拖到第 8 个时间单位才算完带权周转高达 8这就是护航效应的直观体现。SJF/SPN非抢占t0 时只有 P1 到达先跑 P1 到 t7。此时 P2、P3、P4 都已到达服务时间分别是 4、1、4选最短的 P3。P3 跑完后P2 和 P4 服务时间都是 4按到达时间选 P2。P1[0,7] - P3[7,8] - P2[8,12] - P4[12,16]周转P17P210P34P411平均 8.0。带权1、2.5、4、2.75平均 2.5625。对比 FCFSP3 的周转从 8 降到 4改善非常明显。SRT抢占t0 P1 跑剩 7。t2 P2 到达服务时间 4 小于 P1 剩余 5抢占P1 暂停。t4 P3 到达服务时间 1 小于 P2 剩余 2抢占。P3 在 t5 完成。t5 P4 到达比较 P1 剩余 5、P2 剩余 2、P4 服务时间 4选 P2。P2 在 t7 完成接着 P4剩余 4 P1 剩余 5t11 完成最后 P1 跑到 t18。P1[0,2] - P2[2,4] - P3[4,5] - P2[5,7] - P4[7,11] - P1[11,18]周转P118P25P31P46平均 7.5是五种算法里最低的。但 P1 被拖到 18带权周转 2.571长作业的委屈肉眼可见。RRq1模拟过程稍微繁琐我按每个时间单位列一遍队列变化。t0 P1到, 队列[P1] - 跑P1, 剩6 t1 队列[P1] - 跑P1, 剩5 t2 P2到, 队列[P1,P2] - 跑P1, 剩4, 队列[P2,P1] t3 队列[P2,P1] - 跑P2, 剩3, 队列[P1,P2] t4 P3到, 队列[P1,P2,P3] - 跑P1, 剩3, 队列[P2,P3,P1] t5 P4到, 队列[P2,P3,P1,P4]- 跑P2, 剩2, 队列[P3,P1,P4,P2] t6 队列[P3,...] - 跑P3, 完成(周转3) ... t13 - P1完成(周转14) t16 - P4完成(周转11)完整执行序列P1[0,3] P2[3,4] P1[4,5] P2[5,6] P3[6,7] P1[7,8] P4[8,9] P2[9,10] P1[10,11] P4[11,12] P2[12,13] P1[13,14] P4[14,16]P1 在 0-3 连续跑了三个时间片因为它第二次入队后队列里只有它自己。周转P114P211P33P411平均 9.75。注意 P3 虽然服务时间只有 1但因为它到达时队列里已经有 P1、P2 在排所以要等到 t6 才跑上周转 3——RR 的响应性依赖时间片大小和队列长度不是万能药。HRRN非抢占t0 只有 P1跑完到 t7。t7 时算三个进程的响应比R(P2) (7-24)/4 2.25 R(P3) (7-41)/1 4 R(P4) (7-54)/4 1.5选 P3t8 完成。再算R(P2)(8-24)/42.5R(P4)(8-54)/41.75选 P2t12 完成。最后 P4 到 t16。P1[0,7] - P3[7,8] - P2[8,12] - P4[12,16]这组数据下 HRRN 的结果和 SJF 完全一样平均周转都是 8.0。原因在于 P3 足够短响应比一开始就遥遥领先没给其他进程翻盘机会。想看 HRRN 的差异化效果得用 2.5 节那组数据那时它才会把等待久的 P2 提前牺牲一点平均周转换取公平。3.3 横向对比表与三条结论把结果汇总| 算法 | P1周转 | P2周转 | P3周转 | P4周转 | 平均周转 | 平均带权周转 | | FCFS | 7 | 9 | 8 | 11 | 8.75 | 3.500 | | SJF/SPN | 7 | 10 | 4 | 11 | 8.00 | 2.563 | | SRT | 18 | 5 | 1 | 6 | 7.50 | 1.580 | | RR(q1) | 14 | 11 | 3 | 11 | 9.75 | 2.625 | | HRRN | 7 | 10 | 4 | 11 | 8.00 | 2.563 |三条结论值得记下来。第一平均周转时间最优的是 SRT但它让长作业 P1 付出了 18 个单位的代价带权周转 2.571 是 P1 自己的锅也说明抢占式短作业优先对长作业不友好。第二RR 在这组数据里平均周转最差因为它为了响应时间反复排队牺牲了整体效率。第三SJF 和 HRRN 结果重合说明在短作业优势明显的数据里响应比机制的公平性用不上但要记住这只是这一组数据的巧合。4. 用 Python 把五种算法跑通并验证手算结果手算一遍只能验证理解真正要确认没算错得写成代码跑。而且课程设计、面试现场都可能让你白板写一个 RR 或 SRT这套模拟框架可以直接套用。我用 Python 写核心思路是模拟时间推进 事件处理比套公式更接近真实调度器。4.1 进程结构与指标计算先定义进程结构以及五个算法的公共指标计算函数from dataclasses import dataclass dataclass class Process: pid: str arrive: int # 到达时间 service: int # 服务时间 remain: int 0 # 剩余服务时间 start: int -1 # 首次开始执行的时间 finish: int -1 # 完成时间 def __post_init__(self): self.remain self.service def reset(procs): 每跑一个算法前重置状态避免相互污染 for p in procs: p.remain p.service p.start -1 p.finish -1 def metrics(procs): rows [] for p in procs: turn p.finish - p.arrive # 周转时间 wturn turn / p.service # 带权周转时间 wait turn - p.service # 等待时间 rows.append((p.pid, p.arrive, p.service, p.finish, turn, round(wturn, 3), wait)) return rows def summary(rows): n len(rows) avg_turn sum(r[4] for r in rows) / n avg_wturn sum(r[5] for r in rows) / n return round(avg_turn, 3), round(avg_wturn, 3)这里有个坑我踩过reset必须逐个算法调用否则finish、remain会带着上一个算法残留的值算出来的数字看着对其实全错。调试时如果发现某个算法的周转时间离谱地小先检查是不是忘了重置。4.2 五种算法的核心实现FCFSdef fcfs(procs): t 0 for p in sorted(procs, keylambda x: x.arrive): if t p.arrive: t p.arrive # CPU 空转直接跳到下一个到达时刻 p.start t t p.service p.finish tSJF/SPN非抢占每次从就绪集合里挑服务时间最短的def spn(procs): done, t, n set(), 0, len(procs) while len(done) n: ready [p for p in procs if p.arrive t and p.pid not in done] if not ready: t min(p.arrive for p in procs if p.pid not in done) continue p min(ready, keylambda x: (x.service, x.arrive, x.pid)) p.start t t p.service p.finish t done.add(p.pid)注意min的 key 里同时放了service和arrive这是为了处理服务时间相同的场景保证结果可复现。用p not in done会因为 dataclass 做值比较而出错所以统一用pid集合来判断。SRT抢占逐时间单位推进每步都重新选剩余时间最短的def srt(procs): done, t, n set(), 0, len(procs) while len(done) n: ready [p for p in procs if p.arrive t and p.pid not in done and p.remain 0] if not ready: t min(p.arrive for p in procs if p.pid not in done and p.remain 0) continue p min(ready, keylambda x: (x.remain, x.arrive, x.pid)) if p.start 0: p.start t p.remain - 1 t 1 if p.remain 0: p.finish t done.add(p.pid)这里p.remain相等时按到达时间排序等价于相同剩余时间不抢占的约定和手算保持一致。RR时间片轮转def rr(procs, q1): arrived, done, queue, t, n set(), set(), [], 0, len(procs) while len(done) n: # 1. 把 t 时刻及以前到达的进程入队 for p in sorted([x for x in procs if x.arrive t and x.pid not in arrived], keylambda x: x.arrive): queue.append(p) arrived.add(p.pid) # 2. 队列空则时间跳到下个到达时刻 if not queue: t min(p.arrive for p in procs if p.pid not in arrived) continue p queue.pop(0) if p.start 0: p.start t run min(q, p.remain) # 最后一片可能不足 q t run p.remain - run # 3. 这段时间里新到达的进程排在当前进程前面入队 for x in sorted([y for y in procs if p.start y.arrive t and y.pid not in arrived], keylambda x: x.arrive): queue.append(x) arrived.add(x.pid) if p.remain 0: p.finish t done.add(p.pid) else: queue.append(p)队列顺序是 RR 最容易出分歧的地方。我这里的处理是新到达的进程先入队当前进程放队尾等价于把时间片结束瞬间到达的进程算作下一轮。如果你的老师或面试官用的是另一种约定改动第 3 步的顺序即可但整个程序里必须统一。另外run min(q, p.remain)这一句别漏否则最后一个时间片会把进程跑过头剩余时间变成负数。HRRN高响应比优先def hrrn(procs): done, t, n set(), 0, len(procs) while len(done) n: ready [p for p in procs if p.arrive t and p.pid not in done] if not ready: t min(p.arrive for p in procs if p.pid not in done) continue def ratio(p): wait t - p.arrive return (wait p.service) / p.service p max(ready, keylambda x: (ratio(x), -x.arrive)) p.start t t p.service p.finish t done.add(p.pid)ratio就是响应比公式max直接取最大值。-x.arrive是为了响应比相同时让到达更早的优先保证结果稳定。4.3 运行结果核对与一个时间片对比实验拿 3.1 节那组数据跑一遍输出应该和手算完全对上。我实测的结果如下平均周转 / 平均带权周转| 算法 | 平均周转 | 平均带权周转 | | FCFS | 8.75 | 3.500 | | SJF/SPN | 8.00 | 2.563 | | SRT | 7.50 | 1.580 | | RR(q1) | 9.75 | 2.625 | | HRRN | 8.00 | 2.563 |数字对上的那一刻说明前面的手算和代码实现对同一套约定达成了共识。这一步很重要很多人手算没错代码跑出来不一样八成是某个算法的边界约定没对齐。接着做时间片对比实验把 RR 的 q 分别取 1、2、4看指标怎么变| 时间片 q | 平均周转 | 平均带权周转 | P3 带权周转 | | 1 | 9.75 | 2.625 | 3.000 | | 2 | 9.00 | 2.607 | 3.000 | | 4 | 8.50 | 2.741 | 5.000 |能看出来q 从 1 加到 4平均周转一路下降越来越接近 FCFS 的表现但短任务 P3 的带权周转从 3 涨到 5说明时间片一大短任务就得在队列里多等几轮。这就是响应时间和平均效率之间那条此消彼长的曲线。生产环境里选 q就是在这条曲线上找一个适合业务特征的平衡点。5. 常见问题与踩坑记录前面讲了原理和实现这一节集中把高频错误和争议点过一遍。这些都是我在调试和给别人讲课时反复遇到的基本覆盖了考试和实操里的雷区。5.1 时间片到底选多大这是被问得最多的问题。答案的第一层是相对于上下文切换开销来定。如果一次上下文切换要花 0.1 毫秒时间片取 1 毫秒那么切换开销就占了 10% 的 CPU 时间损失太大取 100 毫秒开销占比降到 0.1%可以忽略。所以经验法则是时间片至少要比切换开销大一到两个数量级。第二层是看场景。纯计算型的批处理任务时间片可以取大一点减少切换交互式系统比如终端、GUI要求按键马上有反应时间片就得取小。教材里常给一个参考值80% 的 CPU 突发长度要比时间片短。这话的意思是把时间片设得比大多数进程单次需要的 CPU 时间略大这样大部分进程能在一个时间片内跑完既保证响应又不增加无谓的切换。第三层是别迷信固定值。现代内核很多都用动态时间片根据进程的交互性、优先级、历史行为调整而不是一个写死的常数。但考试里通常还是固定值按题目算就行。5.2 饥饿问题和老化饥饿问题在 SJF 和 SRT 上最明显长作业可能永远排不上。解决思路不外乎几种一是老化Aging随着等待时间增长逐步提高进程的优先级最终让它有机会执行。二是像 HRRN 那样直接把等待时间写进选择公式里。三是在多级队列里用时间片用完降级、等待过久升级的方式。HRRN 的响应比公式1 等待时间/服务时间本质上就是一种连续的老化机制即使一个进程服务时间很长等待时间涨上去后分子也会把响应比顶高。这里有个细节要注意响应比是非抢占的只有在当前进程执行完毕、需要重新选择时才计算所以它治的是轮到选择时的饥饿如果运行中的进程一直不结束它也没办法。5.3 易混点速查表下面这些点几乎每次考试都会出现整理成表格方便对比。它们看起来像细节但往往就是扣分的地方。| 易混点 | 正确理解 | | SJF 和 SPN 的区别 | 大多数教材里 SPN 就是非抢占式 SJF但严格来说 SPN 指最短进程优先侧重进程SJF 侧重作业。考试按题目用词走 | | SJF 和 SRT 的区别 | SJF 非抢占进程一旦开始就跑完SRT 抢占新短进程一到就切 | | FCFS 和 RR 的联系 | q 趋于无穷时RR 退化成 FCFS | | 平均周转 vs 平均带权周转 | 前者可能显示 SJF 最优后者可能在长作业上很惨两者要分开看 | | 抢占式算法是否一定更优 | 不一定抢占有开销而且对长作业不友好平均值好看不代表个体公平 | | 响应比的计算时机 | HRRN 是每次选择前静态计算当前响应比不是持续动态更新 | | 相等剩余时间是否抢占 | 绝大多数约定为不抢占按 FCFS 处理 |再用文字补一句周转时间是从到达算起不是从第一次运行算起。这个坑很隐蔽很多人手算时习惯性地从进程开始运行算到结束结果所有周转时间都少了等待的那一段。正确做法永远是完成时间 - 到达时间。6. 这些算法在真实系统里的影子教材里的五种算法看起来像玩具但它们的思路在真实内核里到处都是。搞清楚映射关系对理解生产系统的行为很有帮助。6.1 从经典算法到现代调度器Linux 早期的 O(n) 调度器和后来的 CFS完全公平调度器跟这五个算法不是一一对应而是把它们的思路揉在一起。CFS 用虚拟运行时间vruntime来排序本质上是按累计占用 CPU 时间做公平分配这就带点 RR 的公平基因又用权重来区分优先级思路接近带权的时间片轮转。它不做严格的短作业优先因为服务时间无法预知但它确实给了短小、交互频繁的任务更好的响应——这是通过虚拟时间的缩放来近似实现的。再看多级反馈队列MLFQ它更像 RR 和 SJF 的混合体短任务在高层队列里用小时间片快速跑完长任务被逐级降到低层队列用更大的时间片运行。这等于用运行行为来近似判断任务长短绕开了预测服务时间这个难题。SRT 那种激进抢占在现代内核里不太常用因为抢占太频繁会带来缓存失效、上下文切换等一连串开销。6.2 从电梯调度看调度思路的通用性有个很有意思的类比磁盘调度里的电梯算法。磁头就像 CPU请求就像进程。SCAN 算法让磁头单向扫过所有请求再回来类似于 FCFS 加了一点顺序优化SSTF最短寻道时间优先直接选离当前磁头最近的请求简直就是磁盘版的 SJF同样存在远处请求被饿死的问题。于是又有 CSCAN、LOOK 等变体来缓解。你会发现调度问题是跨领域的只要存在有限的资源 排队的请求 不同的任务代价FCFS、优先级、公平轮转、折中响应比这几套思路就会反复出现。所以学这五个算法真正有价值的不是背公式而是理解用等待时间补偿服务时间这类折中机制的设计直觉。以后你写任务队列、限流器、连接池策略这套思路照样能用。提示如果你在做课程设计建议在模拟器里加上甘特图可视化和多组数据批量对比两个功能答辩演示时会非常加分也比单组数据更能说明算法的适用边界。最后分享几个我自己的实操体会。手算这类题目我会先在草稿纸上画一条时间轴每处理一个时间点就在轴上标一个刻度而不是凭直觉排序——凭直觉十次有三次会把边界搞错。写模拟器时我会先用一组能心算出答案的极小数据比如两个进程跑通再加进程、加时间片做复杂实验这样出错时能快速定位到是逻辑问题还是边界问题。另外RR 的队列顺序、SRT 的相等剩余时间处理、SJF 的相同服务时间排序这三个约定我建议你在代码开头用注释写死不然过两个月回头改自己都忘了当初是怎么约定的。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。