10 两个完整的虚拟机系统 — VAX/VMS 与 Linux 怎样把零件拼成整车¶
1. 这一章讲什么¶
学完了所有零件,现在看两台真车。VAX/VMS(1970s–80s) 是一台 「硬件有缺陷、软件来补」的经典车:页小到只有 512 字节、页表项连 访问位都没有,VMS 用设计把这两个缺陷都消化掉了。 Linux 是现代车:x86 给什么用什么(硬件多级页表、A/D 位、大页), 同时在替换算法上走出了 2Q 这条自己的路。
在全书链条里的位置:内存虚拟化部分的收束。读完本章, 前面九章的机制都会在真实系统里对上号——也会看到「教科书机制」和 「工程实现」之间的距离。
2. 顶层全景¶
VAX/VMS(1970s,页 512 字节,硬件没有访问位):
混合分段+分页(P0 堆区 / P1 栈区各一张页表,中间空洞不花钱)
+ 用户页表放进内核虚拟内存(表本身也能被换出!)
+ 分段 FIFO + 全局干净/脏页两张第二次机会列表(无访问位时的 LRU 替身)
+ 聚簇写出、按需清零、写时复制
│
Linux(现代,x86 四级页表,硬件有 A/D 位):
4 级页表(48 位有效地址 → 页号劈成 4 段索引)
+ 大页(2MB/1GB)治 TLB 压力,渐进采用
+ 统一页缓存(文件页 + 匿名页一张表)+ 后台回写(pdflush)
+ 2Q 替换(活跃/不活跃两条链表,抗大文件扫描)
图说:上下两台车用的是同一批零件(页表、替换、交换), 但 VMS 的考题是「硬件缺功能」,Linux 的考题是「规模太大」。
3. 核心原理¶
3.1 VAX/VMS:一道具体的难题开篇¶
设定(全是书里的真实数字)。VAX-11 给每个进程 32 位地址空间, 页只有 512 字节 → VPN 23 位、偏移 9 位;VPN 高两位再分 P0(用户程序+堆)/ P1(栈)/ S(内核)三段——混合分段+分页1。 512 字节的页意味着什么灾难?线性页表会有 2²³ 项; 而 VMS 要从微型机跑到大型机通用(书里叫通用性诅咒: OS 要对一大类应用都好,结果往往是对任何一个具体场景都不够好2)。
对策一:P0、P1 各一张页表,基址/界限寄存器指向各表—— 堆栈之间的空洞不花页表空间3。对策二(更狠):用户页表本身 放进内核虚拟内存——页表也可以被换出到磁盘,内存紧时 连「地图」都能腾地方;代价是翻译可能要走两遍表, 全靠硬件 TLB 兜底4。
3.2 没有访问位,怎么做 LRU?——VMS 的两记巧劲¶
VAX 的 PTE 里有有效位、4 位保护、脏位、5 位 OS 保留——没有引用位5。
巧劲一:用保护位「模拟」引用位(Babaoglu 与 Joy,1981)。 把页表里所有页标成「不可访问」;进程一碰就陷入内核, OS 把页恢复成正常保护——这等价于「这页刚被用过」。 隔一阵子没恢复的页,就是久未用的页。要诀是节奏: 标得太多,陷入开销爆炸;标得太少,所有页都被「碰」过一遍, 等于没信息6。
巧劲二:分段 FIFO + 第二次机会列表(segmented FIFO)。 每个进程有驻留集上限 RSS,超了踢本进程的 FIFO 队首—— FIFO 不需要任何硬件支持。但纯 FIFO 太笨,于是踢出的页不直接扔: 干净的进全局干净页表尾、脏的进全局脏页表尾。 别的进程要空闲帧时,从干净表头拿;如果原进程在页被回收前 又缺了它,直接从表里捞回来——省一次磁盘 I/O。 两个全局表越大,这套「分段 FIFO」越像 LRU7。 (对照上一章:这就是「没有传感器时,用软件结构造一个传感器」。)
聚簇(clustering):512 字节的页对磁盘太小,换出时把脏页 攒成大批、一次性写——现代系统的标准做法8。
3.3 两个懒惰优化:按需清零与写时复制¶
按需清零(demand zeroing)。 naive 做法:给堆加页就立即 找物理页、清零(不清零会泄露上个进程的数据!)、映射。 VMS 改成:只放个「不可访问」的表项;进程第一次读写才陷入, 这时才真的分配、清零。进程若从不碰这页,活全免了9。
写时复制(copy-on-write, COW)。复制页的时候不真复制: 两边都映射同一物理页、都标只读。谁要写,陷入内核, OS 现场分配新页、拷数据、给写者私有副本。 fork() 的受益者:复制整个地址空间又慢又蠢,因为 exec() 马上 把大部分覆盖掉——COW 让 fork() 的复制变成「先记账、后兑现」, 语义不变、开销大减10。共享库同理:一份代码页映射进千万进程11。 书里把这叫「懒惰美德」:拖,可能减少当前延迟;拖到底,活可能整个不用干12。
3.4 Linux:现代考题与现代答案¶
地址空间的构造。32 位 Linux 在 0xC0000000 切两半: 以下是用户、以上是内核;内核部分对每个进程都一样(上下文切换只换用户半)。 第 0 页故意标无效——这就是为什么空指针解引用必段错误: VPN 0 无效位,陷入,进程被毙13。
内核地址分两族:内核逻辑地址(kmalloc,与物理内存直接映射,
0xC0000000 对物理 0;连续、不可换出,适合 DMA)
和内核虚拟地址(vmalloc,只在虚拟上连续,物理可离散,适合大缓冲)14。
四级页表。64 位 x86 只用低 48 位:高 16 位空着,低 12 位是偏移, 中间 36 位页号劈成 4 段(P1–P4)逐级索引——一次翻译最坏 走四层表15。内存再大,五级、六级也只是时间问题(书里这句话 已经在被现实兑现)。
大页(huge pages):2MB/1GB 页的存在理由是 TLB 压力, 不是省表——4KB 页下大内存应用把 TLB 塞爆, 有测量显示 10% 的 CPU 周期耗在 TLB miss 上16。 Linux 的采用是渐进式(incrementalism)的范本: 先只给数据库这类刚需应用开显式接口(mmap/shmget 申请), 后来才加透明大页(THP)自动升级17。代价也明白:内部碎片、 与交换不友好18。
统一页缓存与 2Q 替换。Linux 把三类页(内存映射文件、 普通文件读写、匿名堆栈)放进同一张页缓存,脏页由后台线程 (pdflush)定期回写19。替换用的是改进 2Q:两条链表—— 新页进不活跃表,被再次访问才升入活跃表;踢页只在不活跃表挑; 活跃表保持约占总缓存 2/320。它治的病:进程顺序扫描一个大文件 会把 LRU 里所有热页冲光——2Q 让扫描页困在不活跃表里自生自灭21。 表内排序仍用时钟式近似,不做精确 LRU22。
内存映射(mmap):把文件映进地址空间,指针解引用即读写, 没读过的部分触发缺页再读入——文件 I/O 变成了「带缺页的访存」, 持久化部分(本库 15–17 章)和虚拟内存在此合流23。
4. 作者的判断与证据¶
书里给了证据的判断:
- VMS 的全部机制(P0/P1 页表、模拟引用位、分段 FIFO、聚簇) 引原始论文 [BJ81][RL81],可回查567。
- Linux 的 2Q、透明大页、pdflush 都是可验证的实现决策, 书给了参数(活跃表 2/3、2MB/1GB 页、48 位地址)151720。
作者的判断(立场):
- 选 VMS 和 Linux 的理由写得很直白:VMS 教「软件如何用巧思 补硬件的缺陷」;Linux 教「系统在真实负载下如何渐进演化」24。
- 「懒惰」和「渐进」被作者抬到方法论高度——这两个词是整章的题眼。
5. 边界与局限¶
- VMS 是历史系统,教的是设计思维;具体数字(512 字节页、 23 位 VPN)无移植价值。
- Linux 一节是「重要特性巡礼」而非完整实现:缺 NUMA、 memcg、交换算法细节(如 swappiness)等真实世界的主角。
- 本章没覆盖 VM 与安全交织的近年大事件(Meltdown/Spectre 之后 内核页表隔离 KPTI,32 位固定 0xC0000000 那种布局因此退役)—— 成书时这些尚不是主题(补充,不在书里)。
6. 可带走的¶
- 硬件缺什么,软件模拟什么:没有引用位就用保护位造一个; 没有 LRU 就用 FIFO + 第二次机会列表逼近。约束是设计的原料,不是停工的理由。
- 懒惰是系统设计的结构性美德:按需清零、写时复制、延迟回写—— 「拖到不用做」比「做得快」省得多。
- fork+COW 解释了第 02 章埋的疑问:复制整个地址空间为什么不贵。
- 第 0 页标无效 → 空指针必段错误。地址空间布局里的「小设计」 都是调试器与安全的共同设施。
- 统一页缓存的思路:文件页和内存页一张表——「一切都是页」 让替换、回写、统计都只剩一套逻辑。
- Linux 大页的渐进路径是工程决策范本:先显式接口服务刚需, 验证后再自动化——激进的「一步到位」常常死在兼容性和副作用上。
7. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| VAX/VMS 的背景与通用性诅咒 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:35(搜「VAX-11」)与:76(搜「CURSE OF GENERALITY」) |
| 512 字节页 → 页表压力 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:54(搜「512-byte」) |
| P0/P1 各一张页表 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:70(搜「in two」) |
| 页表放进内核虚拟内存 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:92(搜「kernel virtual memory」) |
| 空指针为什么段错误 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:147(搜「NULL POINTER」) |
| PTE 无引用位 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:184(搜「no reference bit」) |
| 用保护位模拟引用位 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:195(搜「EMULATING」) |
| 分段 FIFO 与第二次机会列表 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:213(搜「segmented FIFO」) |
| 聚簇写出 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:229(搜「clustering」) |
| 按需清零 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:241(搜「demand zeroing」) |
| 写时复制 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:257(搜「copy-on-write」) |
| 「懒惰美德」 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:279(搜「BE LAZY」) |
| Linux 0xC0000000 切分 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:348(搜「0xC0000000」) |
| 内核逻辑/虚拟地址 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:355(搜「kernel logical」) |
| 四级页表与 48 位 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:410(搜「four-level」) |
| 大页与 10% TLB miss | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:430(搜「Large Page」)与:445(搜「10%」) |
| 渐进采用大页 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:454(搜「INCREMENTALISM」) |
| 统一页缓存 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:502(搜「unified」) |
| 2Q 替换与活跃表 2/3 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:515(搜「2Q」) |
| mmap 无处不在 | 23. Complete Virtual Memory Systems | text/25-ch23-23-complete-virtual-memory-systems.txt:541(搜「UBIQUITY」) |
| TLB 诊断故事 | 24. Summary Dialogue on Memory Virtualization | text/26-ch24-24-summary-dialogue-on-memory-virtualization.txt:19(搜「TLB」) |
-
出处:「23. Complete Virtual Memory Systems」第 52–64 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:54,搜「512-byte」)。 ↩ -
出处:「23. Complete Virtual Memory Systems」第 76–84 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:76,搜「CURSE OF GENERALITY」)。 ↩ -
出处:「23. Complete Virtual Memory Systems」第 70–94 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:70,搜「in two」)。 ↩ -
出处:「23. Complete Virtual Memory Systems」第 91–100 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:92,搜「kernel virtual memory」)。 ↩ -
出处:「23. Complete Virtual Memory Systems」第 180–185 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:184,搜「no reference bit」)。 ↩↩ -
出处:「23. Complete Virtual Memory Systems」第 195–211 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:195,搜「EMULATING」)。 ↩↩ -
出处:「23. Complete Virtual Memory Systems」第 213–226 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:213,搜「segmented FIFO」)。 ↩↩ -
出处:「23. Complete Virtual Memory Systems」第 227–237 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:229,搜「clustering」)。 ↩ -
出处:「23. Complete Virtual Memory Systems」第 239–255 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:241,搜「demand zeroing」)。 ↩ -
出处:「23. Complete Virtual Memory Systems」第 256–298 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:257,搜「copy-on-write」)。 ↩ -
出处:「23. Complete Virtual Memory Systems」第 268–271 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:553,搜「shared library」)。 ↩ -
出处:「23. Complete Virtual Memory Systems」第 279–292 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:279,搜「BE LAZY」)。 ↩ -
出处:「23. Complete Virtual Memory Systems」第 147–159 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:147,搜「NULL POINTER」)与第 315–353 段(text/25-ch23-23-complete-virtual-memory-systems.txt:348,搜「0xC0000000」)。 ↩ -
出处:「23. Complete Virtual Memory Systems」第 354–388 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:355,搜「kernel logical」)。 ↩ -
出处:「23. Complete Virtual Memory Systems」第 390–428 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:410,搜「four-level」)。 ↩↩ -
出处:「23. Complete Virtual Memory Systems」第 430–446 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:445,搜「10%」)。 ↩ -
出处:「23. Complete Virtual Memory Systems」第 454–478 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:454,搜「INCREMENTALISM」)。 ↩↩ -
出处:「23. Complete Virtual Memory Systems」第 483–493 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:484,搜「internal fragmentation」)。 ↩ -
出处:「23. Complete Virtual Memory Systems」第 497–512 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:502,搜「unified」)。 ↩ -
出处:「23. Complete Virtual Memory Systems」第 513–531 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:515,搜「2Q」)。 ↩↩ -
出处:「23. Complete Virtual Memory Systems」第 516–536 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:516,搜「LRU」)。 ↩ -
出处:「23. Complete Virtual Memory Systems」第 529–531 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:531,搜「approximation」)。 ↩ -
出处:「23. Complete Virtual Memory Systems」第 541–569 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:541,搜「UBIQUITY」)。 ↩ -
出处:「23. Complete Virtual Memory Systems」第 7–28 段(
text/25-ch23-23-complete-virtual-memory-systems.txt:49,搜「effective, working」)。 ↩