ARM64 Linux内核MMU页表配置与TLB管理工程 - 第2节
2. MMU基本概念
在 ARM64 架构中,CPU 执行指令时使用的地址是 虚拟地址(Virtual Address, VA),而实际访问 DRAM 或外设寄存器时使用的地址是 物理地址(Physical Address, PA)。MMU(Memory Management Unit)负责在指令执行流水线的地址阶段,将 VA 实时转换为 PA。
2.1.1 地址空间对比
| 属性 | 虚拟地址(VA) | 物理地址(PA) |
|---|---|---|
| 使用者 | CPU 指令、load/store、PC | 内存控制器、总线、DMA |
| 空间大小 | 48 位(经典页表)或 52 位(LVA 扩展) | 48 位(最大 256 TiB) |
| 连续性 | 每个进程拥有独立连续空间 | 物理内存可能碎片化 |
| 保护粒度 | 页级(4KB/16KB/64KB) | 无直接保护 |
| 映射关系 | 多对一(多个 VA 映射到同一 PA) | 一对一(PA 唯一对应物理位置) |
关键理解: 虚拟地址是“假地址”,物理地址是“真地址”。MMU 是连接两者的桥梁。没有 MMU,CPU 只能直接使用物理地址,无法实现进程隔离和虚拟内存。
2.1.2 ARM64 用户空间与内核空间划分
ARM64 Linux 内核采用 高低端划分 策略:
- 用户空间(EL0): 低地址区域,VA 范围
0x0000_0000_0000_0000 ~ 0x0000_FFFF_FFFF_FFFF(48 位)。 - 内核空间(EL1): 高地址区域,VA 范围
0xFFFF_0000_0000_0000 ~ 0xFFFF_FFFF_FFFF_FFFF(48 位)。 - TTBR0_EL1 指向用户页表基址,TTBR1_EL1 指向内核页表基址。
2.2 地址转换原理
ARM64 地址转换的核心是 多级页表遍历(Page Table Walk)。当 CPU 发出一个 VA,MMU 硬件自动执行以下步骤:
- 确定页表基址: 根据 VA 的最高有效位选择 TTBR0_EL1(用户)或 TTBR1_EL1(内核)。
- 逐级索引: 将 VA 划分为多个位段,每段作为索引查找对应级别的页表项(PTE)。
- 检查权限与属性: 每个 PTE 包含物理地址高位、访问权限、内存类型、脏位、有效位等。
- 获取物理地址: 最后一级 PTE 提供物理页帧号(PFN),与 VA 的页内偏移拼接得到完整 PA。
- 缓存结果: 转换结果存入 TLB,加速后续相同 VA 的访问。
2.2.1 4KB 页 4 级页表地址拆分
| VA 位段 | 位宽 | 作用 | 页表级别 |
|---|---|---|---|
| VA[47:39] | 9 bits | L0 索引(PGD) | Level 0 |
| VA[38:30] | 9 bits | L1 索引(PUD) | Level 1 |
| VA[29:21] | 9 bits | L2 索引(PMD) | Level 2 |
| VA[20:12] | 9 bits | L3 索引(PTE) | Level 3 |
| VA[11:0] | 12 bits | 页内偏移 | — |
硬件加速: ARM64 的 Table Walk 完全由 MMU 硬件完成,无需软件介入。Linux 内核只需维护页表数据结构,MMU 自动读取内存中的页表项进行转换。
2.3 页表的作用
页表(Page Table)是 MMU 地址转换的“字典”,存储在系统内存中。每个进程拥有独立的用户页表,内核页表全局共享。页表的核心作用包括:
- 地址映射: 将虚拟页号(VPN)映射到物理页帧号(PFN)。
- 权限控制: 通过 PTE 中的 AP(Access Permission)位控制读/写/执行权限。
- 内存属性: 设置缓存策略(Normal/Device)、共享属性(Inner/Outer Shareable)、非安全访问等。
- 页表项状态: 有效位(Valid)、脏位(Dirty)、访问位(Access Flag)用于页面回收和写回。
- 大页支持: 在 L1/L2 级别设置块映射(Block Mapping),支持 2MB/1GB 大页,减少 TLB 压力。
2.3.1 ARM64 PTE 关键字段
| 位段 | 位宽 | 名称 | 说明 |
|---|---|---|---|
| [63:48] | 16 bits | PBHA | 页表属性扩展(硬件辅助) |
| [47:12] | 36 bits | PFN | 物理页帧号(4KB 页粒度) |
| [11] | 1 bit | nG | 非全局位(0=全局,1=进程私有) |
| [10] | 1 bit | AF | 访问标志(Access Flag) |
| [9:8] | 2 bits | SH | 共享属性(00=非共享,01=保留,10=外共享,11=内共享) |
| [7:6] | 2 bits | AP | 访问权限(00=EL1 R/W,01=EL1 R/W EL0 无,10=EL1 R/O,11=EL1 R/O EL0 无) |
| [5] | 1 bit | NS | 非安全位(安全世界/非安全世界) |
| [4] | 1 bit | AttrIndx | 内存属性索引(指向 MAIR_EL1) |
| [2:1] | 2 bits | Attr | 页表属性(00=故障,01=页,10=保留,11=块) |
| [0] | 1 bit | Valid | 有效位(1=有效,0=无效) |
注意: 页表项中的 PFN 字段宽度取决于物理地址大小。ARM64 支持最大 48 位物理地址,因此 PFN 占用 36 位(48-12)。如果启用 52 位物理地址扩展,PFN 位段会相应扩展。
2.3.2 页表与 TLB 的协作
页表存储在内存中,MMU 每次地址转换都需要读取内存中的页表项。为了加速这一过程,MMU 内部维护了一个 转换后备缓冲器(TLB),缓存最近使用的 VA→PA 映射。当 TLB 命中时,MMU 直接返回 PA,无需访问内存页表;当 TLB 缺失时,MMU 执行硬件 Table Walk,填充 TLB 后返回结果。
性能关键: 页表遍历是内存访问中最频繁的操作之一。ARM64 通过 TLB、多级页表缓存(如 PTE 缓存)、以及硬件预取机制来降低 Table Walk 的开销。Linux 内核通过
flush_tlb_mm、flush_tlb_page 等接口管理 TLB 一致性。