4. 页表与页表项结构:一级页表、二级页表、页表描述符、属性位

页表是 MMU 实现虚拟地址到物理地址转换的核心数据结构。ARMv7-A 架构支持一级页表(L1)和二级页表(L2)两级查询机制,通过页表描述符(Page Table Descriptor)中的属性位控制内存访问权限、缓存策略和执行特性。本节将详细拆解两级页表的组织方式、描述符格式以及关键属性位的含义。

4.1 一级页表(L1 Page Table)

一级页表也称为“页表目录”(Page Table Directory),其基地址存储在协处理器 CP15 的 TTBR0/TTBR1 寄存器中。一级页表包含 4096 个表项,每个表项 32 位(4 字节),覆盖 4GB 虚拟地址空间。每个一级表项映射 1MB 的虚拟地址块。

关键参数: 一级页表大小 = 4096 项 × 4 字节 = 16KB。必须 16KB 对齐存放于物理内存中。

4.1.1 一级页表描述符类型

一级页表描述符的低 2 位(bits[1:0])用于区分描述符类型:

bits[1:0] 描述符类型 说明
0b00 无效(Fault) 该 1MB 区域未映射,访问将触发缺页异常
0b01 页表项(Page Table) 指向二级页表的基地址,用于 4KB 细粒度映射
0b10 段描述符(Section) 直接映射 1MB 物理内存块,无需二级页表
0b11 超段描述符(Supersection) 映射 16MB 物理内存块,用于大块连续内存
开发建议: 在嵌入式 Linux 内核中,内核空间(高 1GB)通常使用段映射(Section)以节省页表空间并提高 TLB 命中率;用户空间则使用二级页表实现 4KB 页粒度的按需分配。

4.1.2 段描述符(Section Descriptor)格式

当一级描述符类型为段映射(0b10)时,其 32 位格式如下:

位域分布(31 → 0):
[31:20] 物理基地址[31:12]  — 物理地址高 20 位,对应 1MB 对齐
[19:12] 域(Domain)       — 访问控制域标识符
[11:10] 不可用(SBZ)      — 应写 0
[9]     nG(非全局)       — 0=全局,1=进程私有
[8]     S(可共享)        — 多核一致性控制
[7:6]   AP[2:1]           — 访问权限位
[5]     TEX[2]            — 内存类型扩展位
[4]     AP[0]             — 访问权限位
[3]     C(Cacheable)     — 可缓存
[2]     B(Bufferable)    — 可缓冲
[1:0]   0b10              — 段描述符标识
注意: 物理基地址只取高 20 位,意味着段映射的物理地址必须 1MB 对齐。如果物理内存起始地址不是 1MB 的整数倍,将无法使用段映射。

4.2 二级页表(L2 Page Table)

当一级描述符类型为页表项(0b01)时,MMU 会跳转到二级页表进行更细粒度的地址转换。二级页表包含 256 个表项,每个表项 32 位(4 字节),覆盖一级表项对应的 1MB 虚拟地址空间。每个二级表项映射 4KB 的物理页。

关键参数: 二级页表大小 = 256 项 × 4 字节 = 1KB。必须 1KB 对齐存放。

4.2.1 二级页表描述符类型

二级页表描述符的低 2 位(bits[1:0])同样用于区分类型:

bits[1:0] 描述符类型 说明
0b00 无效(Fault) 该 4KB 页未映射
0b01 大页描述符(Large Page) 映射 64KB 物理块(已弃用,兼容 ARMv5)
0b10 小页描述符(Small Page) 映射 4KB 物理页,最常用
0b11 极小页描述符(Tiny Page) 映射 1KB 物理块(已弃用)

4.2.2 小页描述符(Small Page Descriptor)格式

二级小页描述符(0b10)的 32 位格式:

位域分布(31 → 0):
[31:12] 物理基地址[31:12]  — 物理地址高 20 位,对应 4KB 对齐
[11:9]  TEX[2:0]          — 内存类型扩展位
[8]     S(可共享)        — 多核一致性控制
[7:6]   AP[2:1]           — 访问权限位
[5]     nG(非全局)       — 0=全局,1=进程私有
[4]     AP[0]             — 访问权限位
[3]     C(Cacheable)     — 可缓存
[2]     B(Bufferable)    — 可缓冲
[1:0]   0b10              — 小页描述符标识

4.3 两级页表查询流程

以下 SVG 图展示了 ARM MMU 从虚拟地址到物理地址的两级页表查询完整流程:

两级页表查询流程 两级页表查询流程(ARMv7-A) 虚拟地址 VA[31:0] 地址拆分 VA[31:20] → L1 索引 (12位) | VA[19:12] → L2 索引 (8位) | VA[11:0] → 页内偏移 一级页表查询 TTBR + VA[31:20] × 4 → L1 描述符 二级页表查询 L1 基址 + VA[19:12] × 4 → L2 描述符 L1 类型=页表项 段映射:物理地址 = 基址 + 偏移 1MB 粒度,无需二级页表 小页映射:物理地址 = 基址 + 偏移 4KB 粒度,支持按需分配 物理地址 PA[31:0] → 访问内存 属性位检查:权限 / 缓存 / 域

4.4 页表属性位详解

页表描述符中的属性位决定了 MMU 如何对待该内存区域。以下表格汇总了关键属性位的功能:

属性位 位置 功能描述 典型值
AP[2:0] L1[7:6,4] / L2[7:6,4] 访问权限控制:特权/用户读/写 0b111=全读写,0b101=只读
Domain L1[19:12] 域标识(0~15),配合 DACR 寄存器实现域级保护 0=管理域,3=客户域
C / B L1[3:2] / L2[3:2] Cacheable 和 Bufferable 策略 C=1,B=1 写回;C=1,B=0 写通
TEX[2:0] L2[11:9] 内存类型扩展,与 C/B 共同决定内存属性 0b000=普通内存,0b001=设备内存
nG L1[9] / L2[5] 非全局位:0=全局映射(内核),1=进程私有 内核空间=0,用户空间=1
S L1[8] / L2[8] 可共享位:多核一致性维护 1=共享内存区域
XN L1[0](段) 执行禁止位:阻止该区域执行代码 1=不可执行(数据页)

4.4.1 访问权限(AP)位详解

AP 位组合决定了不同特权级下的内存访问能力:

AP[2:0] 特权模式 用户模式 典型用途
0b000 无访问 无访问 保留/故障注入
0b001 读/写 无访问 内核私有数据
0b010 读/写 只读 只读系统调用表
0b011 读/写 读/写 用户进程数据段
0b101 只读 无访问 内核代码段
0b111 只读 只读 用户代码段

4.5 页表配置示例:内核空间段映射

以下代码展示如何在 Linux 内核启动早期设置一级页表段映射,将物理地址 0x80000000~0x80FFFFFF 映射到虚拟地址 0xC0000000~0xC0FFFFFF:

/* 汇编伪代码:设置一级页表段映射 */
/* TTBR0 已指向 16KB 对齐的一级页表基地址 L1_TABLE */

/* 计算一级页表索引:VA[31:20] = 0xC00 >> 20 = 0x300 */
/* 页表项偏移 = 0x300 * 4 = 0xC00 */

LDR   r0, =L1_TABLE          /* 一级页表基地址 */
LDR   r1, =0xC0000000        /* 虚拟地址 */
LSR   r1, r1, #20            /* 右移20位得到索引 */
LSL   r1, r1, #2             /* 乘以4得到字节偏移 */
ADD   r0, r0, r1             /* 指向页表项地址 */

/* 构造段描述符 */
/* 物理基址 0x80000000 >> 20 = 0x800 */
/* 域=0,AP=全读写,C=1,B=1(写回缓存),类型=段 */
LDR   r2, =0x80000000        /* 物理基地址 */
LDR   r3, =0x00000C0E        /* 属性:域0 | AP全读写 | C=1 | B=1 | 段标识0b10 */
ORR   r2, r2, r3             /* 合并基地址和属性 */
STR   r2, [r0]               /* 写入一级页表项 */

/* 使能 MMU 前需执行 DSB 和 ISB 同步指令 */
DSB
ISB
性能提示: 段映射(1MB 粒度)比小页映射(4KB 粒度)占用更少的页表空间和 TLB 条目。对于内核线性映射区域(如直接映射的物理内存),优先使用段映射可显著降低 TLB 缺失率。

4.6 页表属性位与内存类型映射

ARM 架构通过 TEX、C、B 三位的组合定义内存类型,直接影响 CPU 的缓存行为和访问排序:

TEX[2:0] C B 内存类型 典型场景
0b000 0 0 强序(Strongly Ordered) CPU 间同步变量
0b000 0 1 设备内存(Device) MMIO 寄存器
0b000 1 0 正常内存写通(Write-Through) 帧缓冲区
0b000 1 1 正常内存写回(Write-Back) 普通 RAM
0b001 0 0 不可缓存正常内存 DMA 缓冲区
0b001 1 1 写回写分配(Write-Back Write-Allocate) 高性能堆内存
驱动开发陷阱: 将 MMIO 寄存器区域错误配置为“写回”类型会导致 CPU 缓存写操作延迟提交,外设无法及时收到寄存器写入。所有外设寄存器映射必须使用“设备内存”类型(TEX=000, C=0, B=1)或“强序”类型。

4.7 页表配置的调试与验证

在嵌入式 Linux 驱动开发中,可以通过以下方法验证页表配置是否正确:

  • 读取 TTBR0/TTBR1: 通过 MRC 指令读取 CP15 的 c2 寄存器,获取一级页表物理基地址。
  • 遍历页表项: 在内核模块中通过 phys_to_virt() 映射页表物理地址,逐项检查描述符类型和属性位。
  • 使用 /proc/xxx/page_tables: 某些内核调试补丁会导出页表信息到 procfs。
  • JTAG 调试器: 直接读取物理内存中的页表项,比对预期值。

以下 SVG 图展示页表项属性位的位级布局,帮助开发者快速定位配置错误:

二级小页描述符位布局 二级小页描述符(Small Page Descriptor)位布局 物理基地址[31:12] TEX S AP nG AP0 C B 类型 31:12 11:9 8 7:6 5 4 3 2 1:0 各字段说明: • 物理基地址[31:12]:物理页帧号(PFN),对应 4KB 对齐的物理地址高 20 位 • TEX[2:0] + C + B:共同决定内存类型(普通/设备/强序)和缓存策略 • S(Shareable):多核系统中是否共享该页,影响缓存一致性协议 • AP[2:0]:访问权限(特权/用户读/写),由 AP[2:1] 和 AP[0] 组合编码 • nG(non-Global):0=全局映射(内核空间),1=进程私有(用户空间)

4.8 本章小结

  • 一级页表包含 4096 个 4 字节描述符,支持段映射(1MB)和指向二级页表的页表项。
  • 二级页表包含 256 个 4 字节描述符,支持小页映射(4KB),是用户空间内存管理的基础。
  • 页表描述符的属性位(AP、Domain、C/B、TEX、nG、S、XN)精确控制每块内存的访问权限、缓存行为和共享属性。
  • 驱动开发中必须为 MMIO 区域配置设备内存类型,避免缓存导致的外设访问异常。
  • 理解页表结构是调试内存映射问题、优化 TLB 性能和实现自定义内存管理的关键前提。