CPU 怎么决定下一个运行谁?进程调度揭秘
CPU 怎么决定下一个运行谁?进程调度揭秘
你的电脑同时开着浏览器、编辑器、音乐播放器——CPU 只有一个(核),凭什么"同时"服务它们?答案是:每秒切换成百上千次。但"下一个轮到谁"不是随机的,背后是一套精密的裁判规则——进程调度。今天讲透它。
一、调度器在解决什么
就绪队列里排着一长串进程,调度器要回答:这一刻,把 CPU 给谁?
它背负着好几个相互拉扯的目标:
- 公平:每个进程都觉得自己在被照顾
- 吞吐:单位时间干最多的活
- 响应:你按了键,程序多久有反应
- 防饥饿:别让某个进程永远轮不到
这些目标并不总是一致——最典型的拉扯在"公平"和"吞吐"之间。
二、公平 vs 吞吐:一对天生的矛盾
公平优先:尽量让每个进程平分 CPU。好处是交互程序(编辑器、终端)及时响应;坏处是频繁切换——每次切换都有代价(搬寄存器、刷 TLB、冷缓存),切换太勤,大量时间花在"换人"而不是"干活"上。
吞吐优先:让一个进程一口气多跑一会儿。缓存热、切换少,总产出高——批处理、科学计算最喜欢;坏处是长任务占着 CPU,短任务干等,响应差甚至饿死。
像团队排班:轮流上(公平)都不累但交接成本高;一个人干到底(吞吐)总产出高但其他人闲死。调度器就是调"交接频率"的旋钮。
三、两个核心指标
- 周转时间:进程从进队到彻底干完的总时长——衡量吞吐
- 响应时间:进程从进队到第一次被调度——衡量交互体验
这两个指标常常冲突,后面每个调度算法本质都是在它们之间找平衡。
四、代码演示:三种调度策略对比
5 个进程同时到达,运行时间分别是 8、4、2、1、5(单位:时间片):
burst = [8, 4, 2, 1, 5] # 运行时间
def fifo(burst): # 先来先服务
comp, t = [], 0
for b in burst:
t += b; comp.append(t)
return comp
def sjf(burst): # 短作业优先
return fifo(sorted(burst))
def rr(burst, q=1): # 时间片轮转
rem = burst[:]; comp = [0]*len(burst); t = 0
while any(rem):
for i in range(len(rem)):
if rem[i] > 0:
run = min(q, rem[i])
rem[i] -= run; t += run
if rem[i] == 0: comp[i] = t
return comp
def stats(comp):
n = len(comp)
return sum(comp)/n, (sum(comp)-sum(burst))/n
for name, fn in [("FIFO(先来先服务)", fifo), ("SJF(短作业优先)", sjf), ("RR(时间片=1)", rr)]:
comp = fn(burst)
tat, wait = stats(comp)
print(f"{name:<18} 完成时间{comp} | 平均周转 {tat:.1f} | 平均等待 {wait:.1f}")
运行输出:
FIFO(先来先服务) 完成时间[8, 12, 14, 15, 20] | 平均周转 13.8 | 平均等待 9.8
SJF(短作业优先) 完成时间[1, 3, 7, 12, 20] | 平均周转 8.6 | 平均等待 4.6
RR(时间片=1) 完成时间[20, 14, 8, 4, 17] | 平均周转 12.6 | 平均等待 8.6
看懂三个数字背后的取舍:
- FIFO:简单但长作业让短作业干等(平均等待 9.8)
- SJF:平均等待最优(4.6)——但注意运行 8 的长作业被推到 20 才完成,有饥饿风险
- RR:每个进程轮流沾光(8 的进程 20 完成、1 的 4 完成),响应均匀但总周转略差
没有完美的调度器,只有按系统定位选平衡点。
五、Linux 的答案:CFS
Linux 从早期的 O(n) 扫描、O(1) 多级队列,演进到 2007 年的 CFS(完全公平调度器):用一棵按"虚拟运行时间"排序的红黑树,让每个进程获得均等的 CPU 份额——公平优先的集大成。而实时领域(汽车、工业)走另一条 RM/EDF 路线,把"错过时限"当作不可接受的失败。
六、避坑清单
- 别迷信"优先级越高越好":优先级反转(低优先级进程持有锁导致高优先级阻塞)是经典坑,需要优先级继承
- 交互系统看响应、批处理看吞吐:选错指标等于选错调度策略
- 上下文切换不是免费的:时间片设太小,CPU 全花在换人上(RR 的教训)
- I/O 密集 vs CPU 密集要区分:I/O 进程主动让出 CPU,调度器要识别这种"好市民"
- 实时调度别用普通 Linux 内核:要 PREEMPT_RT 补丁或 RTOS,普通内核给不了硬实时保证
七、想系统学操作系统?
本文精选自 ima 知识号【Kruptos】《操作系统内核与驱动详解》订阅库(第 018 期调度基础、第 021 期优先级调度、第 025 期用 Python 仿真调度器等 100 期系统教程,从进程线程、内存管理、调度算法到文件系统与驱动开发,每期配可运行代码)。
📚 完整系列 100 期 + 配套代码,已在 ima 知识号发布
本文只是系列的一个切片。完整系列(100 期系统教程 + 每期可运行代码)在 ima 知识号【Kruptos】持续更新中:
- 🗂 67+ 技术知识库:信号与系统、SDR 软件无线电、数字信号处理、操作系统、AI Agent、大模型微调……几乎覆盖全部软硬件技术栈
- 🧠 8 款 AI 技能:系列生产、知识库管理、CMMI 受管开发、自进化 Agent 等,已在 ima 技能广场上架,即装即用
- ✅ 全部免费订阅,后续更新自动推送
🔍 订阅方式:打开 ima(腾讯智能工作台)→ 搜索「Kruptos」→ 一键订阅。或在 ima 内直接搜索《操作系统内核与驱动详解》等知识库名称。
💬 你线上遇到过调度相关的问题吗(CPU 飙高/响应慢)?评论区聊聊——想看 CFS 细节还是优先级反转,点赞高的安排。
作者:Kruptos(西电毕业,13 年无线通信/DSP/嵌入式科研)|原创内容,转载注明出处
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)