3. 页表层级与多级页表:多级页表设计动机、x86四级页表(PML4/PDPT/PD/PT)、ARMv8-A页表层级
3.1 多级页表的设计动机
在早期32位系统中,线性地址空间为4 GiB,若采用单级页表(即一个巨大的页表数组),每个进程需要维护一个包含约1M个页表项(假设4 KiB页面)的连续物理页表。这种设计存在三个致命缺陷:
- 连续物理内存浪费:单级页表需要占用连续的物理内存(约4 MiB),即使进程只使用很少的虚拟地址。
- 进程切换开销巨大:每次切换进程都需要刷新整个TLB,且页表本身占用大量内存带宽。
- 稀疏地址空间支持差:对于64位地址空间,单级页表的大小将膨胀到无法接受的程度(例如48位地址空间需要约512 GiB的页表)。
多级页表通过按需分配和层级索引解决了上述问题。核心思想是将线性地址拆分为多个索引字段,每一级索引指向下一级页表的物理基址,只有实际映射的虚拟地址范围才会分配对应的页表页。
关键指标对比: 对于4 KiB页面,单级页表需要连续4 MiB物理内存;四级页表(x86-64)在最坏情况下需要约512 KiB页表页,且仅当所有层级都被使用时才分配,实际平均开销远低于单级。
设计动机总结: 多级页表以时间(多次内存访问)换取空间(按需分配页表页),并通过TLB缓存缓解性能损失。
3.2 x86四级页表(PML4/PDPT/PD/PT)
x86-64架构采用四级页表结构,支持48位虚拟地址空间(256 TiB)。线性地址被拆分为5个字段:
| 字段 | 位宽 | 索引级数 | 页表级别 | 每级页表项数 |
|---|---|---|---|---|
| PML4索引 | 9 bit | 第1级 | PML4(Page Map Level 4) | 512 |
| PDPT索引 | 9 bit | 第2级 | PDPT(Page Directory Pointer Table) | 512 |
| PD索引 | 9 bit | 第3级 | PD(Page Directory) | 512 |
| PT索引 | 9 bit | 第4级 | PT(Page Table) | 512 |
| 页内偏移 | 12 bit | — | — | — |
地址转换过程:CR3寄存器保存PML4页表的物理基址。CPU从线性地址中提取PML4索引,在PML4表中找到PDPT页的物理地址;再提取PDPT索引,找到PD页的物理地址;依次类推,最后PT索引给出物理页框号(PFN),加上页内偏移得到最终物理地址。
安全要点: 每一级页表项都包含独立的权限位(R/W、U/S、NX等)。操作系统可以在不同层级设置不同的访问权限,实现细粒度的内存保护。例如,在PDPT级别设置不可执行(NX)可以阻止整个2 MiB区域的代码执行。
3.3 ARMv8-A页表层级
ARMv8-A架构支持多种页表层级配置,通常使用4级页表(与x86-64类似),但具有更灵活的粒度选择。ARMv8-A的页表层级称为Translation Table,从L0到L3:
| 层级 | 名称 | 粒度 | 描述 |
|---|---|---|---|
| L0 | Table Level 0 | — | 顶级页表,覆盖整个地址空间(通常只有1个条目指向L1) |
| L1 | Table Level 1 | 1 GiB块 | 支持1 GiB大页映射,或指向L2页表 |
| L2 | Table Level 2 | 2 MiB块 | 支持2 MiB大页映射,或指向L3页表 |
| L3 | Table Level 3 | 4 KiB页 | 最终页表,提供4 KiB页面映射 |
ARMv8-A与x86-64的关键差异:
- 页表项格式不同:ARM使用Descriptor格式,包含类型(Table/Block/Page)、权限(AP[2:1])、属性(AttrIndx)等字段。
- 权限模型:ARM使用AP(Access Permission)位和PXN(Privileged Execute Never)/UXN(User Execute Never)位实现权限控制。
- 地址空间标识符(ASID):ARM支持ASID,允许不同进程的TLB条目共存,减少TLB刷新开销。
- 转换粒度:ARM支持4 KiB、16 KiB、64 KiB页面粒度(通过TCR寄存器配置)。
安全特性对比: ARMv8-A的PXN/UXN位提供了与x86 NX位类似的功能,但更细粒度——PXN禁止特权级执行,UXN禁止用户级执行。此外,ARM的Stage 2页表(虚拟化扩展)为虚拟机监控器提供了额外的内存隔离层。
3.4 多级页表的性能与安全权衡
多级页表虽然节省了物理内存,但引入了额外的内存访问开销。每次地址转换需要多次访问物理内存(4级页表需要4次内存读取)。TLB(Translation Lookaside Buffer)通过缓存最近使用的页表项来缓解这一开销。
| 因素 | 影响 | 缓解措施 |
|---|---|---|
| 页表层级深度 | 每增加一级,转换增加一次内存访问 | 使用大页(2 MiB / 1 GiB)减少层级 |
| TLB命中率 | TLB未命中导致完整页表遍历 | 增大TLB容量、使用大页、ASID减少刷新 |
| 页表项权限检查 | 每级PTE都需检查权限,增加延迟 | 硬件并行检查、权限缓存 |
| 进程切换 | CR3/TTBR0切换导致TLB失效 | ASID(ARM)/ PCID(x86)避免全局刷新 |
安全实践建议: 在多级页表中,建议在较高层级(如L1/L2)设置严格的权限位(如只读、不可执行),以提供“纵深防御”。即使低层级页表被篡改,高层级的权限限制仍能阻止恶意访问。
3.5 本章小结
- 多级页表通过按需分配页表页,解决了单级页表的连续内存浪费问题,支持稀疏的64位地址空间。
- x86-64使用四级页表(PML4 → PDPT → PD → PT),线性地址拆分为5个字段,CR3寄存器指向顶级页表。
- ARMv8-A使用四级页表(L0 → L1 → L2 → L3),支持1 GiB、2 MiB、4 KiB三种映射粒度,权限模型基于AP/PXN/UXN位。
- 多级页表在节省空间的同时增加了转换延迟,TLB和大页机制是关键的性能优化手段。
- 在安全方面,多级页表允许在每一级设置独立的权限位,实现分层权限控制,增强内存隔离性。