3. MMU 基础:页表、TLB、转换粒度、页表遍历
内存管理单元(MMU)是 CPU 与系统内存之间的地址转换引擎。在 ARM 体系中,MMU 负责将 CPU 发出的虚拟地址(VA)转换为物理地址(PA),同时提供访问权限检查和内存属性控制。理解页表、TLB、转换粒度与页表遍历是掌握 SMMU 与 IOMMU 的基石。
3.1 页表(Page Table)
页表是一种多级查找数据结构,用于描述虚拟地址到物理地址的映射关系。ARMv8-A 架构支持 4KB、16KB 和 64KB 三种页面粒度,并采用 4 级或 3 级页表(取决于配置)。
关键概念:页表项(PTE)不仅包含物理页帧号(PFN),还包含访问权限(AP)、可缓存性(Attr)、执行权限(XN/PXN)等控制位。
3.1.1 页表层级结构
以 4KB 页面、4 级页表(Stage 1)为例,各级页表的作用如下:
| 级别 | 名称 | 粒度 | 覆盖范围 | 描述 |
|---|---|---|---|---|
| L0 | PGD(页全局目录) | 512GB | 512 × 1GB | 顶级页表,每个表项指向一个 L1 表 |
| L1 | PUD(页上级目录) | 1GB | 512 × 2MB | 可映射 1GB 块或指向 L2 表 |
| L2 | PMD(页中间目录) | 2MB | 512 × 4KB | 可映射 2MB 块或指向 L3 表 |
| L3 | PTE(页表项) | 4KB | 512 × 4KB | 最终页表,映射 4KB 物理页面 |
ARM 特色:ARMv8-A 支持 块映射(Block Mapping),在 L1 或 L2 级可以直接映射大块(1GB 或 2MB),跳过后续级别,减少页表遍历深度。
3.1.2 页表项格式
一个典型的 ARMv8-A Stage 1 页表项(L3 描述符)包含以下关键字段:
- 位 [47:12]:输出物理地址(PA[47:12]),即物理页帧号
- 位 [11:10]:访问权限(AP),控制读/写权限
- 位 [7:6]:属性索引(AttrIndx),指向 MAIR 寄存器定义的缓存策略
- 位 [54]:不可执行(XN),禁止在该页执行代码
- 位 [0]:有效位(Valid),标记该表项是否可用
// ARMv8-A L3 页表项结构(简化)
struct arm64_pte {
unsigned long valid : 1; // 位 0: 有效位
unsigned long type : 1; // 位 1: 页/块类型
unsigned long attr_index : 3; // 位 4:2: 属性索引
unsigned long ns : 1; // 位 5: 非安全
unsigned long ap : 2; // 位 7:6: 访问权限
unsigned long sh : 2; // 位 9:8: 可共享性
unsigned long af : 1; // 位 10: 访问标志
unsigned long nG : 1; // 位 11: 非全局
unsigned long pfn : 36; // 位 47:12: 物理页帧号
unsigned long reserved : 15; // 位 62:48: 保留
unsigned long pxn : 1; // 位 63: 特权不可执行
};
3.2 TLB(转换后备缓冲器)
TLB 是 MMU 内部的高速缓存,用于缓存最近使用的虚拟地址到物理地址的转换结果。每次页表遍历都需要访问内存,TLB 可以显著减少地址转换的延迟。
3.2.1 TLB 结构
ARM 处理器通常包含多级 TLB:
- L1 TLB(微 TLB):容量小(通常 32-64 项),访问延迟 1-2 个周期,用于快速路径
- L2 TLB(主 TLB):容量大(通常 512-1024 项),访问延迟 5-10 个周期,作为 L1 的后备
- 统一 TLB:某些实现中 L2 TLB 同时缓存指令和数据转换
TLB 一致性:当页表被软件修改后,必须执行 TLB 失效操作(如
tlbi vmalle1),否则 MMU 可能使用过时的 TLB 项,导致地址转换错误。
3.2.2 TLB 项格式
每个 TLB 项通常包含:
| 字段 | 说明 |
|---|---|
| 虚拟地址(VA) | 转换的输入地址(或 ASID + VA 组合) |
| 物理地址(PA) | 转换后的输出地址 |
| ASID | 地址空间标识符,区分不同进程的 TLB 项 |
| 属性 | 缓存策略、访问权限、可执行性等 |
| 粒度 | 映射的页面大小(4KB/16KB/64KB/2MB/1GB) |
3.3 转换粒度
ARMv8-A 支持多种转换粒度,不同粒度影响页表深度、TLB 覆盖范围和内存利用率。
| 粒度 | 页表级数 | 最大地址空间 | TLB 覆盖 | 适用场景 |
|---|---|---|---|---|
| 4KB | 4 级(L0-L3) | 48 位 VA | 较小 | 通用操作系统,细粒度内存管理 |
| 16KB | 4 级(L0-L3) | 48 位 VA | 中等 | 特定嵌入式场景 |
| 64KB | 3 级(L0-L2) | 48 位 VA | 较大 | 高性能计算,大页需求 |
| 2MB(块) | L2 块映射 | 48 位 VA | 大 | 内核映射,大块连续内存 |
| 1GB(块) | L1 块映射 | 48 位 VA | 极大 | 设备内存映射,IOMMU 大页 |
SMMU 中的大页:在 SMMU 中,使用 2MB 或 1GB 大页可以减少页表级数,降低 TLB 压力,对高带宽 DMA 设备(如 GPU、网络控制器)尤为重要。
3.4 页表遍历(Page Table Walk)
页表遍历是 MMU 根据虚拟地址逐级查找页表的过程。ARMv8-A 的硬件页表遍历器(Hardware Page Table Walker)自动完成此操作,无需软件干预。
3.4.1 遍历流程
- TLB 查找:MMU 首先在 TLB 中查找虚拟地址。如果命中(TLB hit),直接返回物理地址。
- TLB 未命中:如果 TLB 未命中(TLB miss),硬件页表遍历器启动。
- 读取根页表基址:从 TTBR0_EL1(用户空间)或 TTBR1_EL1(内核空间)寄存器获取 L0 页表基址。
- 逐级索引:从虚拟地址中提取各级索引(L0-L3),依次读取页表项。
- 检查块映射:如果在 L1 或 L2 级遇到块映射描述符,遍历提前终止。
- 最终 PTE:到达 L3 级后,读取最终的页表项,获取物理地址和属性。
- TLB 填充:将转换结果写入 TLB,供后续访问使用。
性能影响:一次完整的 4 级页表遍历需要 4 次内存访问(L0-L3),每次访问可能产生缓存未命中。TLB 命中率是 MMU 性能的关键指标。
3.4.2 页表遍历 SVG 图
3.5 地址空间隔离与 ASID
ARM MMU 通过 ASID(地址空间标识符) 实现进程间的 TLB 隔离。每个进程分配唯一的 ASID,TLB 项中记录 ASID,不同进程的相同虚拟地址不会冲突。
- ASID 大小:ARMv8-A 支持 8 位(256 个)或 16 位(65536 个)ASID
- 全局页:nG 位为 0 的页表项标记为全局,所有 ASID 共享(通常用于内核空间)
- 非全局页:nG 位为 1 的页表项与 ASID 绑定,仅对特定进程可见
上下文切换开销:传统做法在进程切换时刷新整个 TLB,导致性能损失。使用 ASID 后,只需切换 TTBR0_EL1 中的 ASID,TLB 项可保留,大幅降低切换开销。
3.6 本章小结
| 组件 | 核心作用 | 关键参数 |
|---|---|---|
| 页表 | 存储 VA→PA 映射关系 | 级数(3/4)、粒度(4K/16K/64K) |
| TLB | 缓存地址转换结果 | 容量、关联度、ASID 支持 |
| 转换粒度 | 决定映射的最小内存单元 | 4KB/2MB/1GB 等 |
| 页表遍历 | 硬件自动查找页表的过程 | 遍历深度、块映射优化 |