2. 页表结构详解
页表是操作系统内存管理单元(MMU)实现虚拟地址到物理地址转换的核心数据结构。在 x86-64 架构中,为了在 64 位线性地址空间下兼顾灵活性与存储开销,采用了 四级页表 的分层结构。本章将深入剖析其层级映射机制、页表项(PTE)的二进制格式以及关键的权限与标志位。
2.1 多级页表的设计动机
如果使用单级页表(即线性页表)管理 48 位虚拟地址空间(2⁴⁸ = 256 TiB),假设页大小为 4 KiB,则需要 2³⁶ 个页表项。每个 PTE 占用 8 字节,单张页表将高达 512 GiB,这在实际系统中是不可接受的。
多级页表通过 按需分配 和 分层索引 解决了该问题:
- 按需分配: 只有实际使用的虚拟地址范围才会分配对应的页表层级。
- 分层索引: 将虚拟地址拆分为多个索引字段,逐级查找,每一级页表仅占用一页(4 KiB)。
核心收益: 对于大多数进程,顶级页目录(PML4)仅需 1 页,中间页目录按需创建,大幅降低内存占用。
2.2 x86-64 四级页表层次结构
x86-64 架构使用 48 位虚拟地址(高 16 位为符号扩展),将其划分为 4 个索引字段 + 页内偏移。四级页表层级如下:
| 层级名称 | 缩写 | 索引位宽 | 页表大小 | 覆盖地址范围 |
|---|---|---|---|---|
| 页映射表 Level 4 | PML4 | 9 位 (bits 47:39) | 4 KiB (512 项) | 512 GiB / 项 |
| 页目录指针表 | PDPT | 9 位 (bits 38:30) | 4 KiB (512 项) | 1 GiB / 项 |
| 页目录 | PD | 9 位 (bits 29:21) | 4 KiB (512 项) | 2 MiB / 项 |
| 页表 | PT | 9 位 (bits 20:12) | 4 KiB (512 项) | 4 KiB / 项 |
虚拟地址的组成结构如下:
63 48 47 39 38 30 29 21 20 12 11 0
+-------------+----------+----------+----------+----------+----------+
| 符号扩展 | PML4 | PDPT | PD | PT | 偏移 |
| (16 bits) | (9 bits) | (9 bits) | (9 bits) | (9 bits) |(12 bits) |
+-------------+----------+----------+----------+----------+----------+
关键理解: 每一级页表都恰好占用一个物理页框(4 KiB),包含 512 个 8 字节的页表项。MMU 通过 CR3 寄存器找到当前进程的 PML4 基址,然后逐级索引,最终获得物理页框号(PFN)。
2.3 页表项(PTE)格式详解
在 x86-64 架构中,每个页表项(PTE)占用 8 字节(64 位)。其格式在不同层级(PML4、PDPT、PD、PT)中基本一致,但某些标志位(如 PS 位)在不同层级含义不同。
PTE 的通用位布局如下:
63 52 51 12 11 9 8 7 6 5 4 3 2 1 0
+------------+---------+---+---+---+---+---+---+---+---+---+---+
| 保留/忽略 | PFN | 忽略| I| D| A| P| U| R| P| W| P|
| (或 NX) | (40位) | | G| T| C| S| W| /| W| /| |
| | | | | | | | | S| | | |
+------------+---------+---+---+---+---+---+---+---+---+---+---+
| | | | | | | | | +-- P (Present)
| | | | | | | | +---- R/W (Read/Write)
| | | | | | | +------ U/S (User/Supervisor)
| | | | | | +-------- PWT (Page Write Through)
| | | | | +---------- PCD (Page Cache Disable)
| | | | +------------ A (Accessed)
| | | +-------------- D (Dirty)
| | +---------------- PS (Page Size)
| +------------------- G (Global)
+--------------------- NX (No Execute)
注意: 在 PML4 和 PDPT 层级,如果 PS 位被置位,则意味着该层级直接映射大页(1 GiB 或 2 MiB),不再继续向下查找。操作系统通常只在 PT 层级使用 4 KiB 标准页。
2.4 权限位与标志位详解
PTE 中的标志位控制着页面的访问权限、缓存策略和状态信息。以下是关键位的详细说明:
| 位域 | 名称 | 功能描述 | 典型用途 |
|---|---|---|---|
| Bit 0 | P (Present) | 页面是否在物理内存中。0 表示缺页(Page Fault)。 | 缺页异常检测、页面换入/换出。 |
| Bit 1 | R/W (Read/Write) | 0 表示只读,1 表示可读写。 | 写保护(Copy-on-Write 机制)。 |
| Bit 2 | U/S (User/Supervisor) | 0 表示仅内核态可访问,1 表示用户态也可访问。 | 内核/用户空间隔离。 |
| Bit 3 | PWT (Page Write Through) | 控制 Cache 写策略:0 回写,1 写通。 | 设备内存映射(需强一致性)。 |
| Bit 4 | PCD (Page Cache Disable) | 1 表示禁止该页被 CPU 缓存。 | MMIO 区域、帧缓冲。 |
| Bit 5 | A (Accessed) | 硬件自动置位,表示该页被访问过。 | 页面置换算法(LRU 近似)。 |
| Bit 6 | D (Dirty) | 硬件自动置位,表示该页被写入过。 | 页面换出时是否需要写回磁盘。 |
| Bit 7 | PS (Page Size) | 在 PD 层级置位表示 2 MiB 大页;在 PDPT 层级置位表示 1 GiB 大页。 | 减少 TLB Miss(大页优化)。 |
| Bit 8 | G (Global) | 全局页,在 TLB 刷新时不被清除(需 CR4.PGE 启用)。 | 内核代码段映射(常驻 TLB)。 |
| Bit 63 | NX (No Execute) | 1 表示禁止在该页上执行指令。 | DEP(数据执行保护)防御缓冲区溢出。 |
实战要点: 操作系统在创建进程时,通常将用户栈和堆页的 U/S 位置 1、R/W 位置 1;代码段页的 U/S 位置 1、R/W 位清 0、NX 位清 0;内核空间页的 U/S 位清 0。A 和 D 位由 CPU 硬件维护,操作系统定期扫描以辅助页面置换决策。
2.5 多级页表的性能权衡
四级页表虽然节省了内存,但引入了额外的内存访问开销。一次地址转换最多需要 4 次内存访问(逐级查找 PTE)。TLB(Translation Lookaside Buffer)正是为了缓解这一开销而设计的硬件缓存。
- 优点: 内存占用低,支持稀疏地址空间,易于扩展(如 5 级页表)。
- 缺点: 转换延迟高(4 次访存),TLB Miss 代价大。
- 优化方向: 使用大页(2 MiB / 1 GiB)减少页表层级;利用 TLB 预取;使用透明大页(THP)。