《操作系统导论》第20章:分页:较小的表 - 深度知识架构
1. 核心矛盾 (The Crucial Problem)
简单的基于数组的页表(通常称为线性页表)太大,在典型系统上占用了过多的物理内存,如何在不增加外部碎片的前提下让页表变小?
2. 核心概念 (Core Concepts)
- 线性页表 (Linear Page Table):
- 定义:一种最简单的页表实现,本质上就是一个数组,操作系统通过虚拟页号 (Virtual Page Number, VPN) 检索该数组以查找页表项 (Page Table Entry, PTE)。
- 角色:分页机制的"基石",但它是引发内存过度消耗的罪魁祸首。
- 多级页表 (Multi-level Page Table):
- 定义:将线性页表变成类似树的数据结构,首先将页表分成页大小的单元,如果不包含有效的 PTE,则根本不分配该页表页。
- 角色:消灭内存浪费的"树形压缩器",支持稀疏地址空间的同时保持极其紧凑。
- 页目录 (Page Directory) 与 页目录项 (Page Directory Entry, PDE):
- 定义:页目录用于记录页表的哪些页被分配了;它由多个 PDE 组成,PDE 包含有效位和物理帧号 (Physical Frame Number, PFN)。
- 角色:多级页表中的"导航员"。如果 PDE 无效,意味着其指向的整页页表项都未分配,从而节省大量空间。
- 反向页表 (Inverted Page Table):
- 定义:不为每个进程保留一个页表,而是整个系统只保留一个页表,其中的项代表系统的每个物理页。
- 角色:页表世界中"更极端的空间节省者",彻底打破了传统的虚拟地址到物理地址的映射视角。
3. 逻辑演进 (Logical Evolution)
为了解决线性页表太大的核心矛盾,计算机系统设计者进行了如下的推导与演进:
- 最初的困境(线性页表太庞大):假设在一个具有 32 位地址空间和 4KB 页的系统中,每个进程的页表就高达 4MB。如果系统有一百个进程,光是页表就会消耗数百兆的内存空间。
- 简单尝试 1(更大的页):通过增大页的大小(如使用 16KB 或 4MB 的页),可以减少页表项的数量,从而直接减小页表体积。
- 遇到的问题:导致严重的内部碎片 (Internal Fragmentation) 问题。应用程序即使只用一点点内存,也会霸占一整个大页,内存很快就会被这些过大的页塞满并浪费。
- 尝试 2(混合方法:分页和分段):为地址空间中的每个逻辑段(代码、堆和栈)分别提供一个页表,并将基址寄存器指向该段页表的物理地址。这种杂合 (Hybrid) 方案使得栈和堆之间未分配的页不再占用页表空间。
- 遇到的问题:它重新引入了外部碎片 (External Fragmentation) 问题。因为每个段的页表变成了任意大小(取决于段的大小),在内存中为这些变长页表寻找空闲空间变得困难。
- 最终成熟方案(多级页表):彻底摒弃分段,引入"间接层 (Level of Indirection)"——页目录。将线性页表切分成多个页大小的块,如果某一整页的页表项无效,就不为它分配物理内存,并在页目录中将其标记为无效。
- 如何克服问题:它按需分配内存,大小与实际使用的地址空间成比例,完美支持稀疏地址空间。同时,由于每个页表部分都能正好放入一页中,操作系统可以直接用空闲页列表轻松管理,彻底消除了外部碎片。对于更大的地址空间,还可以将树的层级扩展到超过两级。
4. 机制与策略 (Mechanisms vs. Policies)
- 底层的"实现手段"(机制 - Mechanisms):
- 硬件多级地址转换机制:在发生地址转换旁路缓冲存储器 (Translation-Lookaside Buffer, TLB) 未命中时,硬件使用虚拟地址的一部分作为页目录索引提取 PDE;如果有效,再用另一部分虚拟地址作为页表索引提取 PTE,最终拼出物理地址。
- 反向页表查找机制:由于需要通过物理页寻找虚拟页,线性扫描极其昂贵,因此底层通常会在此基础结构上建立散列表(Hash Table)来加速查找。
- 上层的"决策逻辑"(策略 - Policies):
- 页表交换策略:当物理内存压力极大时,操作系统可以将位于内核虚拟内存 (Kernel Virtual Memory) 中的部分页表交换 (Swap) 到磁盘上,从而为更重要的数据腾出物理内存。
5. 设计折衷 (Design Trade-offs)
- 牺牲"时间性能",换取"极致的内存空间":这是一个经典的时空折中 (Time-Space Trade-off)。多级页表极大地缩减了内存开销,但在 TLB 未命中时,硬件必须访问内存两次(一次访问页目录,一次访问页表),而纯线性页表只需要访问一次。
- 牺牲"系统极简性",换取"灵活性与高利用率":在多级页表中,无论是硬件还是操作系统去处理 TLB 未命中,其树形查找逻辑都远比线性数组的索引计算要复杂得多。系统容忍了这种复杂性,以此换取对稀疏地址空间的完美支持。
6. 关键洞察 (Key Insights)
- 理解时空折中 (Time-Space Trade-off):在构建系统数据结构时,这几乎是物理定律般的铁则。如果你希望更快地访问特定结构,就必须为该结构付出巨大空间的代价(如线性页表);如果你想节省宝贵的空间,访问过程就注定会变得更慢、更迂回(如多级页表)。
- 页表仅仅是数据结构:不要被底层硬件的术语吓倒。页表并不是什么神秘的魔法,它本质上就是操作系统维护的数据结构。它可以是线性数组,可以是树(多级页表),也可以是散列表(反向页表),甚至可以被当作普通数据一样交换到磁盘里。
- 对复杂性保持极度警惕:好的系统构建者总是实现最小复杂性的系统来完成任务。如果不是因为线性页表的内存浪费实在令人无法忍受,系统绝不应该引入多级页表。正如 Antoine de Saint-Exupery 所言:"完美非无可增,乃不可减。"不必要的复杂性会让系统难以维护和调试。
7. 多级页表架构图

导师的下一步建议:
通过多级页表,我们终于在时间和空间上都驯服了分页机制,为每个进程完美构建了巨大的虚拟地址空间假象。然而,当所有进程所需的内存总和远超物理内存大小时,光靠页表本身已经无法解决问题——我们需要将暂时不用的数据挪到磁盘上。下一章将探讨超越物理内存的机制,学习操作系统如何在内存和磁盘之间交换数据,创造出物理内存无限大的假象。