多级页表原理

多级页表是现代操作系统虚拟内存管理的核心机制,它解决了单级页表在 64 位地址空间下页表过大、内存浪费严重的问题。本节从设计动机出发,逐步剖析多级页表的层级结构与地址翻译过程。

2.1 设计动机:为什么需要多级页表?

在单级页表(线性页表)中,页表是一个连续数组,每个虚拟页对应一个页表项(PTE)。对于 64 位地址空间,若页大小为 4KB,则页表项数量为 252 个,每个 PTE 占 8 字节,页表总大小高达 255 字节(32PB),这显然不可行。即使只考虑进程实际使用的少量虚拟页,单级页表也必须为整个地址空间预留连续物理内存,造成巨大浪费。

核心矛盾: 单级页表要求页表连续存储,而进程实际使用的虚拟地址空间往往是稀疏的。多级页表通过“按需分配”页表层级,只分配实际使用的页表节点,大幅降低内存开销。

多级页表的设计动机可归纳为以下三点:

动机 单级页表问题 多级页表解决方案
内存节省 必须为整个地址空间分配连续页表 按需分配页表层级,未使用的页目录不分配
稀疏支持 地址空间稀疏时页表空洞巨大 层级索引天然支持稀疏映射
扩展性 地址宽度增加时页表大小指数增长 增加层级即可适应更大地址空间

2.2 层级结构:页目录与页表的树形组织

多级页表将虚拟地址划分为多个索引字段,每个字段对应一级页表(页目录)。以 x86-64 架构的 4 级页表为例,虚拟地址被划分为 5 个部分:4 个索引(PML4、PDPT、PD、PT)和页内偏移。每一级页表是一个 4KB 的物理页框,包含 512 个 8 字节的页表项。

层级命名: 不同架构的层级名称不同。x86-64 使用 PML4 → PDPT → PD → PT;ARMv8-A 使用 L0 → L1 → L2 → L3;RISC-V Sv39 使用 L2 → L1 → L0。但原理一致:从根节点逐级索引,直到叶子节点获得物理页框号。

下图展示了 4 级页表的树形层级结构:

4级页表层级结构 4级页表层级结构(x86-64) PML4(第1级) PDPT(第2级) PD(第3级) PT(第4级) 物理页框 层级说明 • 每级页表占 4KB • 每级包含 512 个 PTE • 每个 PTE 指向下一级或物理页 地址划分 VPN[3] → PML4 索引 VPN[2] → PDPT 索引 VPN[1] → PD 索引 VPN[0] → PT 索引

每个页表项(PTE)包含物理页框号(PFN)和权限位(读/写/执行/用户/存在位等)。非叶子节点的 PTE 指向下一级页表的物理地址;叶子节点的 PTE 指向目标物理页框。

2.3 地址翻译过程:从虚拟地址到物理地址

多级页表的地址翻译是一个逐级查表的过程。CPU 发出虚拟地址后,MMU 硬件自动完成以下步骤:

  1. 提取索引: 从虚拟地址中提取各级页表索引(VPN[3] ~ VPN[0])和页内偏移(offset)。
  2. 根节点定位: 从 CR3 寄存器(x86)或 TTBR(ARM)读取顶级页表(PML4)的物理基地址。
  3. 逐级查表: 使用当前级索引在页表中查找 PTE,检查存在位和权限位。若 PTE 有效且指向下一级,则读取下一级页表的物理地址,重复此过程。
  4. 获取物理页框: 到达叶子节点后,从 PTE 中提取物理页框号(PFN)。
  5. 组合物理地址: 将 PFN 与页内偏移拼接,得到最终的物理地址。
缺页异常: 若某级 PTE 的存在位为 0,MMU 触发缺页异常(Page Fault),操作系统负责从磁盘加载数据并更新页表。这是虚拟内存实现“按需调页”的基础。

下图以 x86-64 4 级页表为例,展示完整的地址翻译流程:

4级页表地址翻译流程 4级页表地址翻译流程 虚拟地址:63位 VPN[3] (9位) VPN[2] (9位) VPN[1] (9位) VPN[0] (9位) 偏移 (12位) PML4 查表 PDPT 查表 PD 查表 PT 查表 物理地址 每级查表操作 1. 读取 PTE 2. 检查存在位 & 权限

2.4 多级页表的性能权衡

多级页表虽然节省了内存,但增加了地址翻译的延迟——每次内存访问需要多次查表。以 4 级页表为例,一次内存访问需要 4 次页表查询 + 1 次数据访问,共 5 次内存访问。TLB(Translation Lookaside Buffer)缓存最近使用的页表项,是缓解此问题的关键硬件机制。

指标 单级页表 4级页表 说明
页表大小(64位,4KB页) 32 PB(不可行) 按需分配,通常几 MB 多级页表节省 99.99%+ 内存
翻译延迟(无 TLB) 1 次内存访问 4 次内存访问 多级页表延迟增加 4 倍
翻译延迟(TLB 命中) 1 次 TLB 访问 1 次 TLB 访问 TLB 消除多级查表开销
稀疏地址支持 不支持 天然支持 多级页表是唯一可行方案
TLB 与多级页表的协同: 现代 CPU 的 TLB 命中率通常超过 99%,使得多级页表的翻译延迟几乎被完全隐藏。TLB 缺失时,硬件页表遍历器(Hardware Page Table Walker)自动执行多级查表并填充 TLB,对软件透明。

2.5 不同架构的多级页表对比

主流 CPU 架构均采用多级页表,但层级数量和地址划分略有差异:

架构 层级数 页大小 每级 PTE 数 虚拟地址宽度
x86-64 4 级 4KB / 2MB / 1GB 512 48 位
ARMv8-A 4 级(L0~L3) 4KB / 2MB / 1GB 512 48 位
RISC-V Sv39 3 级 4KB / 2MB / 1GB 512 39 位
RISC-V Sv48 4 级 4KB / 2MB / 1GB 512 48 位

所有架构都支持大页(2MB、1GB),大页可以减少页表层级数(例如 2MB 大页只需 3 级查表),同时提高 TLB 覆盖范围,是性能优化的关键手段之一。

2.6 小结

多级页表通过树形层级结构,将页表从连续数组转变为按需分配的稀疏树,解决了 64 位地址空间下页表过大的根本问题。地址翻译过程由 MMU 硬件自动完成,逐级查表直到找到物理页框。虽然多级查表增加了延迟,但 TLB 缓存和硬件页表遍历器使其在绝大多数场景下高效运行。理解多级页表的原理,是后续学习 TLB 优化、大页支持、硬件 MMU 协同设计的基础。