2. 页表与页表项:页表定义、页表项结构、权限位与属性位
页表是虚拟内存系统的核心数据结构,负责将虚拟地址(VA)映射到物理地址(PA)。在 ARMv8-A 架构中,页表采用多级树形结构,每一级页表由若干页表项(PTE)组成。本章深入剖析页表的定义、页表项的位布局,以及权限位与属性位的硬件语义。
2.1 页表定义与层级结构
页表本质上是一个存储在内存中的数组,每个数组元素称为一个页表项(PTE)。ARMv8-A 支持 4KB、16KB 和 64KB 三种页面粒度,对应不同的页表层级数。以最常见的 4KB 页面 + 48 位虚拟地址空间为例,采用 4 级页表结构:
| 页表级别 | 名称 | 索引位宽 | 页表大小 | 覆盖地址范围 |
|---|---|---|---|---|
| Level 0 | PGD (Page Global Directory) | 9 bits | 512 × 8B = 4KB | 512 GB |
| Level 1 | PUD (Page Upper Directory) | 9 bits | 512 × 8B = 4KB | 1 GB |
| Level 2 | PMD (Page Middle Directory) | 9 bits | 512 × 8B = 4KB | 2 MB |
| Level 3 | PT (Page Table) | 9 bits | 512 × 8B = 4KB | 4 KB |
关键概念: 页表本身也占用物理内存。一个 4 级页表结构在完全填充时,页表自身占用约 4MB 内存(每级 4KB × 512 个表项)。但实际使用中,只有映射的虚拟地址范围才会分配页表,因此内存开销远小于理论最大值。
页表层级与页面大小关系: ARMv8-A 允许在中间级别(Level 1 或 Level 2)直接映射大页(1GB 或 2MB),从而减少页表层级数,降低 TLB 缺失开销。这种机制称为“块映射”(Block Mapping)。
2.2 页表项结构
每个页表项(PTE)在 ARMv8-A 中固定为 8 字节(64 位)。根据页表级别和映射类型,PTE 的位布局略有差异。以下是 Level 3 页表项(指向 4KB 页面的最终映射)的标准结构:
63 54 53 51 50 48 47 44 43 40 39 36 35 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| Upper | PBHA | RES0 | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Lower |
| Attr | [3:0] | | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Attr |
| [63] | | | [11] | [10] | [9] | [8] | [7] | [6] | [5] | [4] | [3] | [2] | [1] | [0] | | | | | | | | | |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| PXN | UXN | Cont | DBM | GP | nG | AF | SH | AP | AP | NS | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr | Attr |
| | | | | | | | [1:0] | [2:1] | [0] | | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx | Indx |
| | | | | | | | | | | | [3] | [2] | [1] | [0] | | | | | | | | | |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+
注意: 上述位图仅为 Level 3 页表项的标准布局。Level 0/1/2 的页表项中,物理地址字段的起始位不同(因为页面大小不同),且可能包含“块描述符”而非“页描述符”。实际编程时,必须使用 ARM 架构参考手册中的精确位定义。
2.3 权限位详解
权限位控制着对映射页面的访问权限,是内存保护的基础。ARMv8-A 页表项中的关键权限位包括:
| 位域 | 名称 | 宽度 | 描述 |
|---|---|---|---|
| [1:0] | Descriptor Type | 2 bits | 00 = 无效(Fault),01 = 页表(Table),10 = 块(Block),11 = 页(Page) |
| [7:6] | AP[2:1] | 2 bits | 访问权限:00 = EL1/EL0 读写,01 = EL1 读写/EL0 只读,10 = EL1 只读/EL0 无访问,11 = EL1 只读/EL0 只读 |
| [5] | AP[0] | 1 bit | 访问权限扩展:0 = 非特权访问受 AP[2:1] 控制,1 = 特权访问受 AP[2:1] 控制 |
| [54] | PXN | 1 bit | 特权执行永不(Privileged Execute Never):1 = 禁止特权模式执行 |
| [53] | UXN | 1 bit | 用户执行永不(User Execute Never):1 = 禁止用户模式执行 |
| [11] | nG | 1 bit | 非全局位:1 = 页表项仅在 ASID 匹配时有效(进程私有) |
权限组合示例:
- 内核代码段: AP[2:1]=10, AP[0]=0, PXN=0, UXN=1 → 仅 EL1 可读可执行,EL0 无访问
- 用户代码段: AP[2:1]=00, AP[0]=0, PXN=1, UXN=0 → EL0 和 EL1 均可读可执行
- 内核栈: AP[2:1]=00, AP[0]=0, PXN=1, UXN=1 → 可读写,不可执行(NX 保护)
2.4 属性位详解
属性位控制着缓存策略、共享属性、内存类型等硬件行为,直接影响系统性能和一致性:
| 位域 | 名称 | 宽度 | 描述 |
|---|---|---|---|
| [4:2] | AttrIndx[2:0] | 3 bits | 内存属性索引,指向 MAIR(Memory Attribute Indirection Register)中的属性配置 |
| [10:8] | SH[1:0] | 2 bits | 共享属性:00 = 非共享,01 = 预留,10 = 外部共享,11 = 内部共享 |
| [10] | AF | 1 bit | 访问标志:1 = 页面已被访问(硬件自动置位,软件可清除用于页面回收) |
| [51] | DBM | 1 bit | 脏位管理(Dirty Bit Management):1 = 启用硬件脏位跟踪 |
| [52] | Cont | 1 bit | 连续页表项提示:1 = 相邻 16 个页表项形成连续映射,可合并为单条 TLB 条目 |
| [50:48] | PBHA[3:0] | 4 bits | 页表项属性提示(Page-Based Hardware Attributes):由具体实现定义,用于自定义硬件优化 |
MAIR 寄存器: ARMv8-A 使用 MAIR_EL1 寄存器定义 8 个内存属性槽位(AttrIndx 0~7)。每个槽位编码了内存类型(Normal/Device)、缓存策略(Write-Back/Write-Through/Write-Combine)和 transient 提示。页表项中的 AttrIndx 字段选择使用哪个槽位。
2.5 页表项结构 SVG 图
下图展示了 ARMv8-A Level 3 页表项的完整位布局,以及关键字段的功能分组:
2.6 页表项权限与属性组合示例
以下示例展示了典型操作系统场景中页表项的配置:
| 内存区域 | AP[2:1] | AP[0] | PXN | UXN | AttrIndx | SH | AF | 描述 |
|---|---|---|---|---|---|---|---|---|
| 内核代码 | 10 | 0 | 0 | 1 | 0 (Normal WB) | 11 | 1 | 仅 EL1 可读可执行,内部共享 |
| 内核数据 | 00 | 0 | 1 | 1 | 0 (Normal WB) | 11 | 1 | EL1/EL0 可读写,不可执行 |
| 用户代码 | 00 | 0 | 1 | 0 | 0 (Normal WB) | 11 | 1 | EL0 可读可执行,EL1 可读 |
| 用户栈 | 00 | 0 | 1 | 1 | 0 (Normal WB) | 11 | 1 | EL0 可读写,不可执行 |
| MMIO 寄存器 | 00 | 0 | 1 | 1 | 4 (Device nGnRE) | 00 | 1 | 非共享,严格顺序,不可缓存 |
| 页表自身 | 00 | 0 | 1 | 1 | 0 (Normal WB) | 11 | 1 | 可读写,内部共享,可缓存 |
重要提示: 页表项中的 AF(访问标志)位必须由软件在首次访问前置位,否则 MMU 会触发 Access Flag 异常。现代操作系统通常通过缺页异常处理程序统一管理 AF 位,实现页面置换和访问计数。
2.7 页表项操作流程
操作系统在建立或修改虚拟地址映射时,需要遵循以下步骤操作页表项:
- 分配页表物理页: 从物理内存分配器获取 4KB 对齐的物理页帧,用于存放页表项数组。
- 清零页表: 将新分配的页表全部写为 0(Fault 描述符),确保所有条目初始为无效状态。
- 设置页表项: 根据映射需求,构造 64 位页表项值,包括物理地址、权限位、属性位和描述符类型。
- 内存屏障: 使用
DSB指令确保页表写入完成,再使用ISB指令刷新指令流水线。 - TLB 失效: 使用
TLBI指令使受影响虚拟地址范围的 TLB 条目失效。 - 验证映射: 通过访问映射地址确认无异常,必要时检查 PAR_EL1 寄存器获取物理地址。
// 示例:设置 Level 3 页表项(ARMv8-A 汇编风格)
// x0 = 页表基地址,x1 = 虚拟地址索引,x2 = 物理地址,x3 = 属性值
// 计算页表项地址
LSR x4, x1, #12 // 提取 VA[47:12] 作为页表索引
AND x4, x4, #0x1FF // 取低 9 位(Level 3 索引)
LSL x4, x4, #3 // 乘以 8(每项 8 字节)
ADD x4, x0, x4 // 页表项地址 = 基地址 + 索引 * 8
// 构造页表项
AND x2, x2, #0x0000FFFFFFFFF000 // 提取物理页帧号 PA[47:12]
ORR x2, x2, x3 // 合并属性位
ORR x2, x2, #0x3 // 设置描述符类型为 Page(11)
// 写入页表项
STR x2, [x4] // 写入内存
// 内存屏障与 TLB 失效
DSB SY // 确保存储完成
TLBI VAE1, x1 // 使虚拟地址对应的 TLB 条目失效
DSB SY // 等待 TLB 失效完成
ISB SY // 刷新流水线
性能优化提示: 批量设置连续页表项时,可以使用
STP(Store Pair)指令一次写入两个 64 位值,并利用 Contiguous 位(Cont=1)提示 MMU 合并 TLB 条目。连续映射的 16 个 4KB 页面可合并为单条 64KB TLB 条目,显著降低 TLB 缺失率。
2.8 页表项与多级页表协同 SVG 图
下图展示了 4 级页表结构中,各级页表项如何协同完成虚拟地址到物理地址的映射: