跳转至

Operating Systems: Three Easy Pieces — 全书拆解

30 秒导读: 这是一本免费公开的操作系统教材(作者官网可直接下载), 回答一个大问题:一台只有几个 CPU、一块内存、一个磁盘的机器, 是怎么变成「每个程序都以为自己独占整台机器」的? 它的讲法与众不同:不是罗列功能,而是把每个设计都还原成一道 「不这么做就会出事」的难题,再一步步推导出解法。 全书三个部分——虚拟化、并发、持久化——其实是一条推理链的三个回合, 每个回合都是同一对矛盾的变奏:既要骗过你,又不能真的失控。

本组文档是我们重写的完整拆解,22 章。读完不必看原书。

1. 先交代清楚:这是谁在什么时候写的

作者 Remzi H. Arpaci-Dusseau 与 Andrea C. Arpaci-Dusseau 夫妇,威斯康星大学麦迪逊分校教授;第 53–57 章安全部分由 UCLA 的 Peter Reiher 撰写
成书 2013 年前后开始发布,持续修订到 2023 年版1
获取方式 作者官网 ostep-book.com 免费发布,读者可下载整本 PDF,也被全球数百所大学用作教材
风格 每章开头有一个「教授与学生」的对话剧,用玩笑把本章的问题先演一遍;正文里反复出现「难题框」(crux),把要解决的问题先立起来再求解

两件事必须先说,否则会误读这本书:

第一,它是一本「为什么」的书,不是「是什么」的清单。 它几乎每个设计都要 先回答「不这样会出什么事」——进程切换要回答「程序捣乱怎么办」, 分页要回答「地址中间有一大段空洞怎么办」,文件系统要回答「写一半断电怎么办」。 读它的时候,结论可以忘,这个提问顺序要带走。

第二,它刻意不追新。 书里花大量篇幅讲 1962 年的 CTSS、1978 年的 VAX/VMS、 1984 年的 FFS——作者明说这是有意的:「50 年前的有些想法,到今天仍然值得知道」2。 具体数字(磁盘转速、页大小)会过时,推理链不会。

2. 全书一条主线(读完这一节,你就懂了这本书)

这本书是一条从头贯到尾的推理链。三步一个回合:先立一个幻觉, 再承认幻觉有两个死穴,再逐个补上机制。 不看后面任何一章,只读这一节, 你也能把这本书讲给别人听。

① 出发点:用户要的其实是个幻觉

程序真正做的只有一件事:从内存取一条指令、执行、取下一条3。 但用户要的体验是「我同时开着浏览器、音乐和一百个标签页」。 一台机器只有几个 CPU、一块内存、一个磁盘——供不应求,是这里一切设计的出发点。

操作系统的回答是一张支票:给你虚拟化的资源——虚拟的 CPU、 虚拟的内存、虚拟的磁盘——「让每个用户都以为自己在独享系统」4。 这张支票能不能兑现,就是全书要答的问题。

② 方法论:每个设计都拆成两半

作者的工具箱里只有两把刀:机制(怎么做)和策略(做哪个)。 书里原话:「低层的机器叫机制」——上下文切换(把一个程序存档、换上另一个)、地址翻译是机制; 「高层的智慧」——决定换谁运行、换谁出内存,是策略5。 这个分法贯穿全书:先把机制讲到能用,再在机制之上讨论策略的取舍。 读任何系统的设计文档,先问「这两半分别是什么」,一半说不清的设计都有问题。

③ 第一回合:CPU 的幻觉——跑得快,与管得住,天然冲突

要让程序跑得快,最自然的做法是让程序直接在硬件上跑—— 书里叫直接执行6。但这样一来操作系统就管不住它了: 它可以永远跑下去不撒手,可以碰不该碰的内存。 这是全书第一个、也是最根本的一个矛盾:效率与控制不可兼得。

解法是一个复合体(第 03 章):平时不拦它,但时钟每隔几毫秒 就强行插一脚,把控制权抢回来;程序想动操作系统的奶酪,得先 自投罗网式地发起一个系统调用。操作系统像一个神经质的管家: 「你可以随便用这间屋子,但我每几毫秒就进来看一眼」—— 书里管这叫「偏执」(paranoia),并说这是操作系统的日常7。

④ CPU 幻觉的另一半:轮到我之前,先排队

机制解决「怎么换」,策略解决「换谁」。这里的坏消息是: 想排队排得好,你得先知道未来——哪个作业短、哪个作业急, 而操作系统恰恰不知道。于是书里推出两个最有生命力的答案: 用历史猜未来(MLFQ:最近用过大量 CPU 的,将来大概率还用, 那就往后排;第 04 章),和干脆不猜、按比例分(彩票调度: 每毫秒开奖,票多的赢面大;第 05 章)。 两条路都不完美,但都能用——这就是策略问题的常态。

⑤ 第二回合:内存的幻觉——既要保护,又要灵活

CPU 的管法不能照搬到内存:程序在内存里随手一指,就是一条地址, 硬件来不及请示操作系统。所以内存的钥匙必须做进硬件里: 每条地址都在送到内存之前被翻译一遍,翻译表只有操作系统能改8。 程序于是被关进自己的地址空间——它的每个指针都只能指向自己的地盘。

但翻译本身很快露出两个新窟窿:翻译表太大(存不下), 翻译本身太慢(每条指令都要多查一次内存)。整本书中段就是 填这两个窟窿的接力赛:分段填地址中间的空洞(第 07 章), 分页填碎片(第 08 章),TLB 这个硬件小抄把翻译提速(第 08 章), 多级页表把表瘦身(第 09 章),交换让磁盘假装成内存的一部分(第 09 章)。 最后第 10 章把零件拼成两台真机器:VAX/VMS 和 Linux。

⑥ 第三回合的前半:当「同时」是真的同时

前面所有魔术,都建立在「同一时刻其实只有一个在跑」上。 多核来了,这个前提塌了:两个线程真的在同一纳秒各跑各的。 这时冒出一个新世界级的麻烦:一句 加一 这样的小事, 在两个线程手里会被拆成三步,交错执行,加着加着就丢了。 锁(第 12 章)、条件变量(第 13 章)、信号量(第 14 章), 就是把这三步重新捆成一气的全部家当——而它们各自的代价 (慢、会死锁、会等错)构成了并发部分的全部内容。

⑦ 第三回合的后半:会断电的世界

内存、CPU 的幻觉有个共同的底气:搞砸了,重启就好。 磁盘不行——用户的数据存在那里,断电、系统崩溃、磁盘老化, 都是文件系统必须当面回答的问题。 这里的矛盾同样是一对: 磁盘慢得离谱(机械臂找数据比 CPU 慢一百万倍量级), 又脆得离谱(随便哪次写入都可能只写了一半)。 文件系统的全部技艺就是这对矛盾的变奏:局部性把要一起用的 东西摆在一起(第 16、17 章),日志把「改两个地方」 变成「先记账、后改动」,断电后照着账本重放(第 17 章), 冗余与校验和(第 15、18 章)对付磁盘静默说瞎话。

⑧ 终章:把幻觉延伸到别的机器上

一台机器的把戏玩熟了,自然想把多台机器连成一台更大的: 这就是分布式系统。但网线那头不是内存总线—— 消息会丢、会迟到、会重复,对面的机器会毫无预兆地死掉。 书里的问题框原文是:「怎么用不总好使的零件,造一个好使的系统」9。 答案依然是同一招:承认失败会发生,用无状态(对方不记任何关于你的事)和幂等(重试几次效果一样)这样的 约定,把失败变成一件「重试一次就好」的小事(第 19 章); 再用同一套「目标→策略→认证→访问控制→密码学」的梯子, 处理跨机器的信任(第 20–22 章)。

⑨ 一句话收尾

这本书从头到尾其实在重复同一件事:

先想清楚「不这么做会出什么事」,再决定值得花多大的代价去防。

虚拟化是「不出事」的幻觉;并发和持久化是「出了事怎么办」的学问; 安全是「有人故意让它出事」的版本。三条主线,一个脑子。

3. 二十二章地图

这张表不用记任何术语——每章名字后面,写的是「读完你能回答什么问题」。

章 读完你就能回答
01 这本书回答什么问题 操作系统到底在忙活什么?「机制」和「策略」凭什么成为全书的骨架?
02 进程 「一个程序在跑」到底是什么意思?fork() 为什么会一次调用、返回两次?
03 受限直接执行 操作系统怎么做到既让程序全速跑、又能随时把它摁停?
04 调度(上) 「公平」和「快」为什么经常打架?MLFQ 怎么在什么都不知道的情况下两头兼顾?
05 调度(下) 怎么保证 A 拿到 75%、B 拿到 25%?多核为什么让调度反而更难?
06 地址空间 每个程序的「私有内存」错觉是怎么立的?malloc 和 free 底下发生什么?
07 基址、界限与分段 用两个寄存器就能隔离程序,为什么后来不够用了?
08 空闲空间与分页 碎片是怎么回事?固定大小的页为什么能终结它,代价又是什么?
09 交换与页替换 内存装不下怎么办?该把谁踢出去,凭什么?
10 两个完整的虚拟机系统 VAX/VMS 和 Linux 怎样把前面所有零件拼成能跑的系统?
11 并发与线程 线程和进程差在哪?为什么共享内存既方便又危险?
12 锁 一条 加一 怎么会在两个线程手里变没?锁靠什么实现?
13 加锁的数据结构与条件变量 怎么等一个「现在还不成立」的条件,而不把 CPU 烧掉?
14 信号量与并发 bug Dijkstra 的一个原语怎么包打天下?真实项目里的并发 bug 长什么样?
15 I/O 与磁盘 CPU 怎么不用死等慢设备?几块磁盘怎么拼得又快又可靠?
16 文件与文件系统实现 「文件」底下是什么?inode 和目录怎么拼出整个文件树?
17 崩溃一致性与日志 写一半断电,磁盘上的状态怎么救回来?
18 闪存盘与数据完整性 没有机械臂的存储 device 有什么新脾气?数据悄悄坏了怎么发现?
19 分布式系统与 NFS/AFS 网络那头靠不住时,怎样仍让文件系统「看起来正常」?
20 安全引论与认证 系统怎么知道「你是谁」?密码为什么既是答案又是问题?
21 访问控制与密码学 「谁能做什么」怎么判定?操作系统管不到的地方靠什么保护?
22 分布式安全 在可能被窃听的网络上,怎样证明「对面确实是我朋友」?

推荐顺序: 01 → 22 顺序读,这也是原书的推理顺序。 只想拿主线的话,读本页第 2 节,再按需跳读。

4. 这本书覆盖什么、不覆盖什么

覆盖(而且讲得比多数教材清楚)——读完你能回答:

  • 为什么操作系统的一切设计都从「资源供不应求」出发;
  • 进程、地址空间、文件这三样东西各自「虚拟」掉了什么;
  • 上下文切换、系统调用、中断、地址翻译、分页、TLB 这些机制各自堵的是哪个窟窿;
  • 调度、页替换这类策略问题为什么通常没有最优解,只有明确的假设下的权衡;
  • 并发编程为什么难、难在哪几种具体形状上;
  • 文件系统怎样同时对付「慢」和「会断电」,日志为什么成为现代文件系统的标配;
  • 分布式系统为什么必须围绕「部分失败」设计,NFS 和 AFS 两种相反的设计各换来什么;
  • 安全为什么被拆成目标→策略→认证→访问控制→密码学五级台阶。

不覆盖(别指望在这本里找):

缺什么 说明
怎么动手写一个操作系统 附录有实验指导(xv6 等),但正文只讲设计与推理,不带你写代码
网络协议本身 分布式部分假定网络存在,不讲 TCP/IP 怎么工作
具体语言的运行时 全书以 C/UNIX API 为例,不讲任何托管语言(Java/Go/Rust)的内存与线程模型——但读它会明白那些模型在替你挡什么
密码学的数学 只讲用法、威胁模型和会犯的错,算法内部一步不进
追新的实现细节 容器、虚拟化的新进展、eBPF 等都不在;作者刻意以经典系统为承重案例

5. 我们的判断

判断(我们的,不是书里的): 这本书最值钱的不是任何一章的知识, 而是它示范的提问顺序:每个设计先问「不这样会出什么事」, 再问「防它的代价谁来付」。这套顺序可以直接搬去评审任何系统或语言的设计—— 包括编程语言本身(所有权在防什么?GC 的停顿在付什么代价?)。 如果错,会错在: 有些设计是「先有了锤子再找钉子」的历史产物, 倒推「它防的窟窿」会赋予它并不存在的合理性;以及,教材为了可教 会把连续的工程史切成干净的章节,真实的演化比这乱得多。

判断(我们的,不是书里的): 它的案例选择(CTSS、VAX/VMS、FFS、LFS、NFS/AFS) 本身就是一部「什么设计活了下来」的筛选史:活下来的共同点是把语义变简单 (无状态、日志、地址翻译放进硬件),而不是把实现变聪明。 这个规律对今天设计协议和语言仍然成立。 如果错,会错在: 这是幸存者偏差的读者视角——被淘汰的设计里 也许同样有把语义变简单的,只是死在了时机、生态或专利上。

判断(我们的,不是书里的): 安全部分(第 53–57 章)出自另一位作者之手, 风格从「逐层推导」变成「全景扫射」,深度比前三部分浅,但五级台阶 (目标→策略→认证→访问控制→密码学)的骨架很稳,适合当地图用、不适合当终点用。 如果错,会错在: 这低估了「全景」的价值——安全恰恰需要广度优先, 只深推一条链容易在别处留洞;不过就教材定位而言,它确实没给足 每层的具体机制(比如 capability 系统的实现细节)。

6. 许诺兑现表

这份拆解里每一处「第 N 章讲」,都在这里记一行、逐行核过。

许诺在哪 许诺了什么 应兑现在哪 核过了吗
本页 §2 ③ 效率与控制的矛盾怎么解(时钟、系统调用、上下文切换) 03 §3–§5 ✓
本页 §2 ④ 用历史猜未来(MLFQ)、按比例分(彩票) 04 §4–§5、05 §2–§3 ✓
本页 §2 ⑤ 分段填空洞、分页填碎片、TLB 提速、多级页表瘦身、交换扩内存 07–09 各章 ✓
本页 §2 ⑤ 两台真机器把零件拼起来 10 全章 ✓
本页 §2 ⑥ 加一被拆成三步与锁、条件变量、信号量 12–14 各章 ✓
本页 §2 ⑦ 局部性、日志、冗余与校验和 16–18 各章 ✓
本页 §2 ⑧ 无状态、幂等与跨机器信任的五级台阶 19 §4–§5、20–22 各章 ✓

拆解写于 2026-10-19,依据 2023 年版(ostep-book.com 免费 PDF)。原始书籍文件不入库, 本组文档是我们自己的重写;每条引用都可用 node scripts/book-verify.mjs ostep 回核。


  1. 出处:「Preface」第 11 段(text/01-fm-preface.txt:11,搜「virtualization, concurrency, and persistence」)。前言交代三个主题即虚拟化、并发、持久化;版本沿革见「Preface」第 103 段(text/01-fm-preface.txt:103,搜「Version 1.00」),2018 年 1.00 版、2019 年 1.01 版、2023 年 1.10 版。 ↩

  2. 出处:「23. Complete Virtual Memory Systems」第 18 段(text/25-ch23-23-complete-virtual-memory-systems.txt:18,搜「50 years old」)。原文说有些 50 年前的想法「仍然值得知道」。 ↩

  3. 出处:「2. Introduction to Operating Systems」第 10 段(text/04-ch02-2-introduction-to-operating-systems.txt:10,搜「executes instructions」)。原文:运行中的程序做的只有一件事——执行指令,处理器每秒从内存取指、译码、执行,如此往复。 ↩

  4. 出处:「3. Dialogue on Virtualization」第 23 段(text/05-ch03-3-dialogue-on-virtualization.txt:23,搜「virtual peaches」)。教授用「分桃子」作比:给每个食客一个「虚拟桃子」,每人以为独占,实际只有一颗真桃。 ↩

  5. 出处:「4. The Abstraction: The Process」第 29 段(text/06-ch04-4-the-abstraction-the-process.txt:29,搜「low-level machinery」)。原文:低层的方法叫机制,高层做决策的部分书中称 policy(策略)。 ↩

  6. 出处:「6. Mechanism: Limited Direct Execution」第 28 段(text/08-ch06-6-mechanism-limited-direct-execution.txt:28,搜「direct execution」)。「直接执行」就是直接把程序放在 CPU 上跑。 ↩

  7. 出处:「11. Summary Dialogue on CPU Virtualization」第 26 段(text/13-ch11-11-summary-dialogue-on-cpu-virtualization.txt:26,搜「Paranoia rules the day」)。对话里学生总结:操作系统相当偏执,要确保自己始终掌控机器。 ↩

  8. 出处:「15. Mechanism: Address Translation」第 4 段(text/17-ch15-15-mechanism-address-translation.txt:4,搜「limited direct execution」)。内存虚拟化沿用受限直接执行的思路:大部分时候让程序直接在硬件上跑,关键处介入。 ↩

  9. 出处:「48. Distributed Systems」第 18 段(text/50-ch48-48-distributed-systems.txt:18,搜「parts that don’t work correctly」)。难题框原文:如何用不能一直正常工作的组件造出能工作的系统。 ↩