3. 虚拟内存与物理内存:页表的作用与多级页表结构

虚拟内存是现代操作系统实现进程隔离、简化内存管理、支持大地址空间的核心机制。CPU 发出的地址是虚拟地址(VA),必须通过硬件-软件协同的地址转换机制,映射到实际的物理地址(PA)。页表(Page Table)正是这一转换的核心数据结构。

3.1 页表的核心作用

页表本质上是操作系统为每个进程维护的一张映射表,记录了虚拟页号(VPN)到物理页框号(PFN)的对应关系。每次内存访问,MMU(Memory Management Unit)都会查询页表完成转换。

作用维度 具体说明
地址转换 将虚拟地址映射到物理地址,实现进程地址空间隔离
权限控制 页表项(PTE)中记录读/写/执行权限,保护内核和进程数据
按需分页 通过 Present 位标记页面是否在物理内存中,支持缺页中断和交换
共享内存 不同进程的虚拟页可映射到同一物理页框,实现高效共享
性能视角: 页表查询是每次内存访问的必经之路。如果页表完全由软件遍历,每次访存将增加数十次内存读取。因此,硬件 TLB(Translation Lookaside Buffer)作为页表的缓存,是降低地址转换延迟的关键。

3.2 单级页表的问题

在 32 位系统中,虚拟地址空间为 4GB,若页面大小为 4KB,则需约 1M 个页表项。每个 PTE 占用 4 字节,单级页表大小约为 4MB。对于 64 位系统(如 x86-64),虚拟地址空间达到 256TB,单级页表将膨胀到无法接受的大小(数 GB 甚至 TB 级)。

  • 内存浪费: 每个进程都需要连续存储整个页表,即使大部分虚拟地址空间未被使用。
  • TLB 覆盖范围小: 单级页表导致 TLB 只能缓存少量映射,TLB 缺失率升高。
  • 上下文切换开销大: 切换进程时需刷新 TLB,重新加载页表。

3.3 多级页表结构

多级页表通过分层索引,将页表拆分为多个层级,只有实际使用的虚拟地址范围才会分配页表页,大幅减少内存占用。以 x86-64 的 4 级页表为例:

虚拟地址(48位)分解:
+-----------+-----------+-----------+-----------+-----------+
|  PML4     |  PDPT     |  PD       |  PT       |  Offset   |
|  (9 bit)  |  (9 bit)  |  (9 bit)  |  (9 bit)  |  (12 bit) |
+-----------+-----------+-----------+-----------+-----------+
页大小:4KB
页表层级:PML4 → 页目录指针表 → 页目录 → 页表 → 物理页
关键点: 每级页表本身也是一个 4KB 的物理页,包含 512 个 8 字节的页表项(9 位索引,2^9=512)。未使用的虚拟地址范围对应的中间级页表页不会被分配,从而节省大量内存。
x86-64 四级页表地址转换流程 x86-64 四级页表地址转换流程 虚拟地址(48位) PML4 (9 bit) PDPT (9 bit) PD (9 bit) PT (9 bit) Offset PML4 表 PDPT 表 PD 表 PT 表 物理页 说明: 每级页表均为 4KB 物理页,包含 512 个 8 字节 PTE。只有实际使用的虚拟地址范围才会分配中间级页表页。 最终通过 PT 表中的 PTE 获得物理页框号,与 Offset 拼接得到完整物理地址。

3.4 多级页表的性能权衡

多级页表虽然节省了内存,但增加了地址转换的深度:每次访存需要多次内存读取(4 级页表需要 4 次内存访问)。TLB 的存在正是为了缓解这一开销。

对比维度 单级页表 多级页表(4级)
内存占用 连续大块,浪费严重 按需分配,节省内存
转换深度 1 次内存访问 4 次内存访问(无 TLB 时)
TLB 缺失代价 较低(仅 1 次内存读) 较高(4 次内存读 + 页表遍历)
大地址空间支持 不支持(64位不可行) 支持 48/57 位地址空间
进程隔离 每个进程独立页表 每个进程独立页表树
性能陷阱: 多级页表在 TLB 缺失时,硬件需要遍历 4 级页表,每次遍历都是一次内存访问。如果工作集大且访问模式随机,TLB 缺失率会急剧上升,导致显著的性能下降。perf 工具中的 dTLB-load-misses 和 iTLB-load-misses 事件正是用于捕捉这类问题。

3.5 页表项(PTE)结构

每个页表项不仅包含物理页框号,还包含多个标志位,用于控制页面行为和权限。

x86-64 PTE 结构(8字节):
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
| P  | R/W| U/S| PWT| PCD| A  | D  | PS | G  |  Reserved  |  PFN (物理页框号)   |
| 0  | 1  | 2  | 3  | 4  | 5  | 6  | 7  | 8  |  9-11      |  12-63              |
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+

关键标志位说明:
- P (Present): 页面是否在物理内存中
- R/W: 读写权限(0=只读,1=读写)
- U/S: 用户/超级用户权限
- A (Accessed): 页面是否被访问过(硬件自动设置)
- D (Dirty): 页面是否被写入过(硬件自动设置)
- PS (Page Size): 页面大小(0=4KB,1=2MB/1GB 大页)
perf 分析关联: Accessed 和 Dirty 位由硬件在首次访问/写入时自动设置,操作系统利用这些位实现页面置换算法(如 LRU 近似)。在 perf 分析中,page-faults 事件与 Present 位密切相关——当访问的页面不在内存中时,触发缺页中断,导致显著的延迟。

3.6 大页(Huge Pages)与多级页表

多级页表支持不同大小的页面。x86-64 支持 4KB(标准页)、2MB(大页)和 1GB(巨页)。使用大页可以减少页表层级,降低 TLB 缺失率。

页面大小 页表层级数 TLB 覆盖范围(典型 64 条目) 适用场景
4KB 4 级 256KB 通用场景,内存碎片控制好
2MB 3 级(跳过 PT 级) 128MB 数据库、HPC、大内存应用
1GB 2 级(跳过 PD 和 PT 级) 64GB 虚拟化、大型科学计算
优化建议: 对于内存密集型应用(如 Redis、数据库、NFV 数据面),启用透明大页(Transparent Huge Pages, THP)或显式使用大页,可以显著降低 TLB 缺失次数,提升内存访问性能。perf 可以通过 dtlb_load_misses.stlb_hit 等事件评估大页带来的收益。
大页 vs 标准页 TLB 覆盖范围对比 大页 vs 标准页 TLB 覆盖范围对比 标准页 4KB TLB 条目数:64 覆盖范围:64 × 4KB = 256KB 页表层级:4 级 大页 2MB TLB 条目数:64 覆盖范围:64 × 2MB = 128MB 页表层级:3 级 512x 覆盖 性能影响: 使用 2MB 大页后,相同 TLB 条目数下覆盖范围扩大 512 倍,大幅降低 TLB 缺失率。 同时页表层级减少 1 级,TLB 缺失时的页表遍历代价也相应降低。 perf 事件:dtlb_load_misses.stlb_hit 可评估大页对二级 TLB 命中率的影响。

3.7 总结:页表与 TLB 性能的关系

  • 页表是地址转换的基石,多级结构在内存占用和转换深度之间取得平衡。
  • TLB 是页表的硬件缓存,其命中率直接决定内存访问延迟。
  • 大页技术通过减少页表层级和扩大 TLB 覆盖范围,是降低 TLB 缺失的有效手段。
  • perf 分析中,TLB 缺失事件(如 dTLB-load-misses)是定位内存访问延迟瓶颈的关键指标。