ARM MMU 地址翻译

本文通过一次完整的 L1→L2 页表遍历实例,深入解析 ARMv7-A MMU 的地址翻译机制、TLB 缓存体系、内存属性编码及多任务上下文切换的底层细节。

1. MMU 的核心职能

Memory Management Unit(MMU)的首要职责是地址重定位:将处理器发出的虚拟地址(Virtual Address, VA)翻译为主存中的物理地址(Physical Address, PA)。ARM MMU 由硬件自动完成翻译,对应用程序完全透明。

除地址翻译外,MMU 还同时控制三项关键属性:

  • 内存访问权限(Access Permissions):谁可以读/写/执行这个页面?
  • 内存类型与 Cache 策略:这块内存是 Normal、Device 还是 Strongly-ordered?
  • 内存序(Memory Ordering):访问顺序是否需要严格保证?

虚拟内存带来的工程收益:

  1. 隔离:每个任务拥有独立的虚拟地址空间,物理上共存但逻辑上互不可见
  2. 透明性:应用编写、编译和链接全部基于虚拟地址,无需知晓物理布局
  3. 连续假象:即使物理内存碎片化,对应用而言虚拟空间是连续的
  4. 按需映射:当 MMU 无法翻译一个地址时,会触发 abort 异常,OS 可借此实现按需页面分配(demand paging)

当 MMU 被禁用时,所有虚拟地址直接映射到对应的物理地址(flat mapping)。这意味着尽早使能 MMU 是引导代码的重要目标之一。

1

1.1 使能 MMU

在使能 MMU 之前,必须先完成以下准备工作:将页表写入内存,并将 TTBR(Translation Table Base Register)设置为指向页表。使能 MMU 的代码序列如下:

1
2
3
MRC  p15, 0, R1, c1, C0, 0       ; 读取 SCTLR 控制寄存器
ORR R1, R1, #0x1 ; 置位 bit[0](M bit)
MCR p15, 0, R1, c1, C0, 0 ; 写回 SCTLR,使能 MMU

这里需要特别注意:如果使能 MMU 会改变当前正在执行的代码所在区域的地址映射(例如从 flat mapping 切换到虚拟地址空间),必须在使能操作前后插入适当的 Barrier 指令保证操作顺序,具体可参考 ARM Architecture Reference Manual 中的推荐序列。

然而,每次 MMU 翻译都直接去遍历内存中的页表——那将意味着每次 LDR/STR 指令都额外触发多次外部内存读取。ARM 的解决方案是在 MMU 内部再加一层与 L1 Cache 类似的高速缓存:TLB。


2. TLB:地址翻译的硬件缓存

每次内存访问都做一次完整的页表遍历(Translation Table Walk)代价巨大——一次 L1+L2 两级遍历需要多次外部内存读取。Translation Lookaside Buffer(TLB)是 MMU 内部的页表缓存,缓存最近使用的虚拟地址到物理地址的翻译结果。

2.1 TLB 的层级结构

ARM 实现通常采用两级 TLB 架构:

1
2
3
4
5
6
Micro-TLB (I-side, 8 entries)     ← near I-Cache, 0-cycle hit
Micro-TLB (D-side, 8 entries) ← near D-Cache, 0-cycle hit
↓ (miss)
Main TLB (~64 entries) ← unified, extra latency
↓ (miss)
Hardware Table Walk ← auto-traverse page tables in memory

Micro-TLB 的典型配置是 8 条映射(I-side 和 D-side 各 8),命中无需任何额外周期。Main TLB 容量更大(典型 64 条),但命中需要额外延迟。这与 L1/L2 Cache 的分层逻辑完全一致。

和 Cache 类似,TLB 也有自己的替换策略(replacement policy),但这对软件完全透明——只需要知道:TLB 满时,硬件会自动选择旧条目驱逐,为新条目腾出空间。

2.2 TLB 条目内容

2

每条 TLB 条目不仅仅存储 VA→PA 的映射关系,还包含从页表条目中提取的完整属性:

1
2
3
+----------+------+-------------------------------+
| VA tag | ASID | PA | TEX|C|B|AP|nG|XN|
+----------+------+-------------------------------+

例如当前运行 Process A (ASID=5),其用户空间地址 0x0000_1000 被映射到一个 4KB 页。这条 TLB 条目在硬件中的实际内容如下:

1
2
3
4
5
6
7
VA tag     = 0x00001 (VA[31:12], 4KB aligned)
ASID = 0x05
PA = 0x40000_000 (Physical Page Base)
TEX = 000, C = 1, B = 1 → Normal WB, no WA
AP[1:0] = 11, APX = 0 → Full access (R/W)
nG = 1 → Non-global (process-private)
XN = 0 → Executable

注意 TLB 中存储的 VA tag 只取 VA 的页号部分(bits[31:12]),页内偏移(bits[11:0])不在 TLB 的 tag 比较中,因为 4KB 页内的所有地址共享同一条翻译——这类似于 CPU Cache 的 tag/index/offset 划分逻辑。

2.3 TLB 一致性与维护

ARM 架构的一条核心规则:只有有效的页表条目才能被缓存到 TLB,无效条目(Fault 条目)永远不会进入 TLB

这条规则决定了修改页表后是否需要刷 TLB。分两种情况:

情况一:修改已有的有效条目(例如改变一个已映射页面的权限或物理地址)。旧翻译已在 TLB 中,修改内存中的页表后 TLB 保留的是过期数据——必须 invalidate。

这里有一个容易被忽略的细节:造成 TLB 填充的不仅是代码中显式的 LDR/STR——speculative 指令预取和数据读取也可能触发页表遍历,把翻译装进 TLB。这意味着即使你认为某个地址”最近没有被访问过”,它的翻译仍然可能已经在 TLB 里(被推测执行悄悄填进去了)。因此修改任何有效条目后都必须 invalidate,不能依赖”最近没访问所以 TLB 里没有”的假设。这也是内核 TLB 维护路径看起来比其他架构”保守”的原因之一。

情况二:将无效条目改为有效条目(例如 OS 按需分配一个新页面——之前该 VA 对应 Fault 条目,现在填入有效映射)。因为 Fault 条目从未进入 TLB,TLB 中根本不存在这个地址的缓存,所以无需 invalidate。第一次显式访问会自然触发 TLB miss → 页表遍历 → 装载新条目。

Linux 内核中使用 flush_tlb_all()flush_tlb_range() 来完成 TLB 维护,这些函数在底层使用 CP15 的 TLB 无效化操作:

1
2
3
4
5
; 全局无效化所有 TLB 条目
MCR p15, 0, r0, c8, c7, 0 ; TLBIALL

; 按虚拟地址无效化特定条目
MCR p15, 0, r0, c8, c7, 1 ; TLBIMVA, r0 包含虚拟地址

TLBIALL 使整个 TLB 全部失效,开销最大,通常只在上下文切换的最后手段中使用(例如 ASID 分配耗尽时不得不全局刷新)。TLBIMVA 按需精准清理——MMU 硬件在 TLB 中搜索匹配指定 VA 的条目并只移除那一项,其他进程的缓存不受影响。代价权衡:全刷简单但波及全局,按地址刷精准但每次需要额外一次 CP15 写操作。

TLB 缓存的是翻译结果,但它缓存的”粒度”取决于页表本身的配置。ARM 提供了从 4KB 到 16MB 四种页大小——每一种都直接影响 TLB 的覆盖效率。


3. 页大小的工程权衡

ARMv7-A 短描述符格式支持四种粒度:

粒度 大小 备注
Supersection 16 MB 16 个连续 L1 条目,虚拟/物理地址均需 16MB 对齐
Section 1 MB 单个 L1 条目直接映射
Large Page 64 KB L2 条目的 16 次重复
Small Page 4 KB L2 条目标准粒度

选择页大小是经典的精细度 vs TLB 覆盖率权衡:

  • 小页面(4KB):精细控制权限和 Cache 属性,减少内部碎片。但 TLB 条目覆盖的内存范围小,TLB miss 更频繁。
  • 大页面(1MB/16MB):单条 TLB 条目覆盖大片内存,TLB 命中率高。适合大块连续内存(如内核代码段、GPU 帧缓冲)。

此外,每个 L2 翻译表需要 1KB 内存——若使用大量 4KB 页面的精细映射,L2 表本身的物理内存开销也不可忽视。


4. 第一级地址翻译:Section 映射

ARMv7-A 使用两级页表架构,未启用 LPAE 时采用短描述符格式。该格式具有以下约束:

  • 32-bit 页描述符
  • 最多两级翻译表(L1 + L2)
  • 支持 32-bit 物理地址空间
  • 支持 16MB/1MB Section 和 64KB/4KB Page

超出 32-bit 物理地址范围的系统需要启用 LPAE 长描述符格式(不在本文范围内)。下面的讨论全部基于短描述符。

4.1 L1 翻译表结构

L1 翻译表(也称 Master Translation Table)将 32-bit 虚拟地址的 4GB 空间划分为 4096 个 1MB 的 Section,每个 Section 对应一个 32-bit(4 字节)的条目。因此 L1 表正好是 4096 × 4 = 16 KB,且必须 16 KB 对齐。

基地址存储在 CP15 的 Translation Table Base Register(TTBR)中(CP15 c2)。

3

4.2 定位 L1 条目

以 ARM 文档中的标准示例为例,走一遍完整的 L1 查表过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
给定:
L1 翻译表基地址 = 0x1230_0000 (位于 CP15 c2: TTBR0)
虚拟地址 VA = 0x0010_0000

Step 1: 取 VA 高 12 位 [31:20]
0x0010_0000 >> 20 = 0x001
这意味着该地址属于第 1 个 1MB Section (从 0 开始编号)

Step 2: 计算表内偏移
条目编号 × 条目大小 = 0x001 × 4 = 0x004

Step 3: 得出条目物理地址
0x1230_0000 + 0x004 = 0x1230_0004
MMU 从此地址读取 32-bit 的 L1 条目

4.3 L1 条目完整格式

4

L1 条目的最低两位 [1:0] 决定了条目类型。以下是四种类型及其判断逻辑:

bits[1:0] 类型 含义
00 Fault 未映射,触发 abort 异常
01 L2 Page Table 条目指向一个 L2 翻译表的物理基地址
10 Section (1MB) 或 Supersection (16MB) 条目直接给出物理地址高 12 位
11 保留

Supersection(16MB)的判断依赖 bit[18]:若为 1 则该条目是 Supersection 的一部分,需要 16 个连续相同条目。

三类有效条目的完整 32 位布局(位宽表示从高位到低位的划分):

Fault 条目(bits[1:0] = 00):所有高位被忽略,访问即 abort。

L2 页表指针(bits[1:0] = 01):

1
2
3
4
5
[31:10]  L2 Descriptor Base Address    — 指向 L2 表物理基地址(1KB 对齐)
[9] P bit — ECC 相关
[8:5] Domain[3:0] — Domain ID
[4:2] SBZ — Should-Be-Zero
[1:0] 01 — 类型标识

Section 条目(bits[1:0] = 10):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[31:20]  Section Base Address           — 物理 Section 基地址 [31:20]
[19] SBZ — Should-Be-Zero
[18] 0=Section, 1=Supersection — 类型细分
[17] nG — Non-Global 标识
[16] S — Shareable 标识
[15] APX — 访问权限扩展
[14:12] TEX[2:0] — 类型扩展
[11:10] AP[1:0] — 访问权限
[9] P bit — ECC 相关
[8:5] Domain[3:0] — Domain ID(参见 6.5 节)
[4] XN — Execute Never
[3] C — Cacheable
[2] B — Bufferable
[1:0] 10 — 类型标识

Supersection 条目:与 Section 相同,但 bit[18]=1,且 Section Base Address 域使用 bit[31:24](而非 bit[31:20])作为物理基地址,为 16MB 对齐提供更多高位地址位。

4.4 从 Section 条目生成物理地址

5

Section 映射的一个核心特征是:VA 的 bits[19:0](低 20 位,1MB 范围内偏移)直接穿越到物理地址,不做翻译。这是因为物理 Section 基地址总是 1MB 对齐——物理端 bits[19:0] 永远为 0,所以无需从页表中读取,直接用 VA 的低 20 位即可。

实例:完整的 Section 翻译

1
2
3
4
5
6
7
8
9
10
11
VA = 0x0010_0420  (1MB Section #1, 页内偏移 0x420)
TTBR = 0x1230_0000

L1 条目地址: 0x1230_0000 + (0x001 × 4) = 0x1230_0004
读取条目值: 假设 = 0x2000_0C0E
bits[31:20] = 0x200 → 物理 Section 基地址 = 0x2000_0000
bits[1:0] = 10 → Section 条目

物理地址 = 0x2000_0000 | (0x0010_0420 & 0x000F_FFFF)
= 0x2000_0000 | 0x0000_0420
= 0x2000_0420

全部翻译在单次 L1 查表后即完成,无需进入 L2——这正是 Section 映射的效率所在。


5. 第二级翻译表

当 L1 条目的 bits[1:0] = 01 时,条目指向一个 L2 翻译表。

5.1 L2 表结构

  • 大小:256 个 32-bit 条目 = 1 KB,必须 1KB 对齐
  • 粒度:每个条目覆盖 4KB 虚拟地址范围(1MB ÷ 256 = 4KB)
  • 索引位:VA bits[19:12](8 位,对应 256 个条目)

5.2 L2 条目完整格式

66

L2 条目也有三类,由 bits[1:0] 区分:

bits[1:0] 类型 含义
00 Fault 未映射
01 Large Page (64KB) 一个条目覆盖 64KB,需在表中连续重复 16 次
1x (1011) Small Page (4KB) 标准 4KB 页,bit[0] 的差异仅影响 XN 位位置

Small Page 条目(bits[1:0] = 1x)的完整位布局:

1
2
3
4
5
6
7
8
9
[31:12]  Small Page Base Address        — 物理页帧号(4KB 对齐)
[11:10] nG, S — 排序由 bit[0] 决定
[9] APX — 访问权限扩展
[8:6] TEX[2:0] — 类型扩展
[5:4] AP[1:0] — 访问权限
[3] C — Cacheable
[2] B — Bufferable
[1] 1 — 类型标识固定为 1
[0] XN (bit[1]=0 时) / 1 (bit[1]=1 时) — Execute Never

Large Page 条目(bits[1:0] = 01)的完整位布局:

1
2
3
4
5
6
7
8
9
10
11
[31:16]  Large Page Base Address         — 物理页帧号(64KB 对齐)
[15] XN — Execute Never
[14:12] TEX[2:0] — 类型扩展
[11] nG — Non-Global 标识
[10] S — Shareable 标识
[9] APX — 访问权限扩展
[8:6] SBZ — Should-Be-Zero
[5:4] AP[1:0] — 访问权限
[3] C — Cacheable
[2] B — Bufferable
[1:0] 01 — 类型标识

5.3 完整的 L1→L2 两级遍历

7

以下是与上节相同虚拟地址(0x0010_0420)在 L2 映射下的完整翻译过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
VA = 0x0010_0420

Step 1 — L1 查表 (bits[31:20] = 0x001)
TTBR = 0x1230_0000
L1 条目地址 = 0x1230_0000 + 0x004 = 0x1230_0004
读取 L1 条目 = 0x3000_0001
bits[1:0] = 01 → 这是一个 L2 页表指针
bits[31:10] = 0x3000_0 → L2 表基地址 = 0x3000_0000

Step 2 — L2 查表 (bits[19:12] = 0x00)
L2 条目地址 = 0x3000_0000 + (0x00 × 4) = 0x3000_0000
读取 L2 条目 = 0x4000_042E (bit[1]=1 → Small Page)
bits[31:12] = 0x4000_0 → 物理页帧号 = 0x40000
物理页基地址 = 0x4000_0000

Step 3 — 生成最终物理地址
物理地址 = Physical Page Base[31:12] + VA[11:0]
= 0x4000_0000 + 0x0420
= 0x4000_0420

对比两个实例可以看到关键区别:Section 映射一次 L1 查表即完成(翻译覆盖 1MB 连续区域),两级页表需要 L1→L2 两次内存读取,但能做到 4KB 粒度的独立控制。

8


6. 内存属性

页表条目不仅负责地址翻译——它还编码了该内存页面的访问权限、内存类型和 Cache 策略。

6.1 访问权限(AP/APX)

AP[1:0] + APX 组合定义了每个页面在特权模式(Privileged)和非特权模式(Unprivileged/User)下的访问权限:

APX AP Privileged Unprivileged 描述
0 00 No access No access 任何访问均触发 abort
0 01 Read/Write No access 仅内核可读写
0 10 Read/Write Read 用户态只读(常用于 .rodata
0 11 Read/Write Read/Write 完全访问
1 01 Read No access 特权只读
1 10 Read Read 全局只读

当访问违反权限时,数据访问产生 precise data abort,指令预取在指令实际执行前产生 prefetch abort。CP15 的 Fault Address Register 和 Fault Status Register 会记录故障地址和原因,abort 处理器据此决定:是修复页表后重试(如写时复制/COW),还是终止违规进程。

6.2 内存类型:Normal / Device / Strongly-ordered

ARMv7 定义了三种互斥的内存类型:

内存类型 可缓存 关键行为
Strongly-ordered 所有访问严格按程序顺序执行。所有访问假定为 Shareable
Device 所有访问按程序顺序,但允许提前到/自其他 Device 区域。用于内存映射外设寄存器
Normal 可缓存 最灵活——可 speculative access、可乱序、可合并、可预取。普通 RAM

Strongly-ordered 和 Device 内存的关键差异:如果不区分两者,所有外设访问都将被当作 Strongly-ordered 处理——这意味着对两个独立设备的访问也必须严格串行化,导致不必要的性能损失。Device 类型的”程序序”仅保证到同一目标的访问被串行化。

6.3 TEX/C/B 编码详解

页表条目中的 TEX[2:0]、C 和 B 位共同定义了内存类型和 Cache 策略:

TEX C B 内存类型 说明
000 0 0 Strongly-ordered
000 0 1 Device (Shareable) 外设寄存器
000 1 0 Normal, Inner/Outer WT, no WA Write-Through, 无写分配
000 1 1 Normal, Inner/Outer WB, no WA Write-Back, 无写分配
001 0 0 Normal, Inner/Outer non-cacheable 不可缓存
010 0 0 Device (Non-shareable) 旧式非共享设备
1bb a a Normal Cached, TEX[2]=1 时 TEX[1:0]=Outer policy, C/B=Inner policy

最后一行(TEX[2]=1 时)支持内外层 Cache 策略的独立编码。此时 TEX[1:0]、C 和 B 四个位共同构成扩展编码,并由 ARM 定义的映射表解析为 Inner 和 Outer Cache 属性。通常可以理解为:TEX[1:0] 主要编码 Outer Cache 策略,C/B 主要编码 Inner Cache 策略,但最终属性应以 ARM ARM 的 TEX/C/B 映射表为准,而不是分别独立解释。

值得注意的是,某些 Cortex-A8/Cortex-A15 实现并未区分 L1/L2 的 Cache Policy,而是将页表中的 Inner Cacheability 同时应用于 L1 和 L2,因此 Outer 属性不会真正控制 L2 的写策略。

6.4 Execute Never (XN)

XN 位阻止从指定内存地址执行代码。一旦设置:

  • 禁止 speculative 指令预取(防止预取设备寄存器引发副作用)
  • 若 CPU 尝试执行该页面中的代码,触发 prefetch abort

典型用法:所有 Device 和 Strongly-Ordered 内存区域一律标记 XN=1,用户态数据页(堆、栈)设置 XN=1 防止栈溢出 ROP 攻击。

6.5 Domains(已弃用但需了解)

ARM 架构的 Domain 机制提供了 16 个 Domain ID,每个 Domain 在 DACR(Domain Access Control Register, CP15 c3)中有 2-bit 权限:

  • No-access:任何对属于该 Domain 页面的访问均 abort,忽略页面自身权限
  • Manager:忽略页面权限,完全访问
  • Client:使用页面自身的权限设置

Domain 在 ARMv7 中已被标记为 deprecated,但为确保权限正确生效,仍需为每个 Section 分配 Domain 并将 DACR 对应字段设为 Client。典型的初始化做法:所有页面的 Domain 字段填 0,DACR 所有位置为 Client(0x55555555)。


7. 多任务与 MMU

在多任务 OS 中,每个进程拥有独立的页表。当调度器切换进程时,MMU 需要从”进程 A 的虚拟地址空间”切换到”进程 B 的虚拟地址空间”。ARM 为此提供了 ASID 和 TTBR0/TTBR1 双基址寄存器两套机制。

7.1 ASID:避免上下文切换刷 TLB

每个进程被 OS 分配一个 0-255 的 Address Space ID(ASID),存储在 CP15 c13 的 ASID 寄存器中。

当翻译一个标记为 non-global(nG=1) 的页面时,MMU 不仅需要 VA→PA 匹配,还需要当前 ASID 与 TLB 条目中存储的 ASID 一致。这带来一个深刻的好处:上下文切换时无需刷 TLB

1
2
3
4
5
6
7
Process A (ASID=5) ──── TLB ──── Process B (ASID=7)
VA 0x1000 VA 0x1000
↓ ↓
TLB[ASID=5, VA=0x1000] TLB[ASID=7, VA=0x1000]
↓ ↓
PA 0x3000_1000 PA 0x5000_1000
(hit, ASID match) (hit, ASID match)

没有 ASID 的话,每次上下文切换必须刷掉整个 TLB,新进程的每个首次访问都触发一次完整的页表遍历——这对上下文切换频繁的 Linux 系统而言是灾难性的性能损失。

标记为 global(nG=0) 的页面(如内核代码段和公共库)则不受 ASID 约束——所有进程的 TLB 查询都能匹配这些全局条目,进一步减少 miss。

7.2 TTBR0 和 TTBR1

每个进程需要独立的 L1 页表(16KB),其中大部分条目(内核空间映射)在各进程间完全相同。ARM 提供了 两个 Translation Table Base Register(TTBR0 和 TTBR1)来解决这个冗余问题。

TTB Control Register(CP15 c2)中设置一个参数 N(0-7),决定 VA 如何分配到两个 TTBR:

1
2
VA top N bits all zero → use TTBR0 (user space)
VA top N bits not all zero → use TTBR1 (kernel space)

实例:N=7 时,VA 的高 7 位为 0 意味着地址在低 32MB 范围内——这部分由 TTBR0 指向的进程私有页表覆盖。其余 4GB-32MB 空间由 TTBR1 指向的全局内核页表覆盖。

效果:

  • 进程私有的用户空间页表(TTBR0)只需包含 32 个条目(128 字节),而非完整 4096 条
  • 内核空间映射在 TTBR1 中只维护一份,所有进程共享
  • 上下文切换只需修改 TTBR0 和 ASID

但这里存在一个微妙的原子性问题:TTBR0 和 ASID 的更新是两个独立的 CP15 写操作。如果 speculative 访问在新 TTBR0 + 旧 ASID 或旧 TTBR0 + 新 ASID 的不一致状态下发生,可能导致错误的 TLB 填充。ARM Architecture Reference Manual 推荐了特定的指令序列来处理这一竞态。

7.3 FCSE(已弃用)

Fast Context Switch Extension(FCSE)在 ARMv4 引入但自 ARMv6 起弃用。它通过将进程 ID 替换 VA 高 7 位来避免上下文切换时刷 Cache/TLB——这本质上是 ASID 的前身。新代码应使用 ASID + TTBR0/TTBR1 机制,FCSE 仅供维护遗留系统时参考。

FCSE 启用的系统中,ARM 文档会区分 Modified Virtual Address(MVA)Virtual Address(VA):MVA 是 VA 经过 FCSE 进程 ID 替换后的地址,VA 是处理器原始发出的地址。这一区分仅当 FCSE 启用时有意义,在 FCSE 禁用(现代系统)时 MVA 等同于 VA。


8. 关键要点

  1. MMU 不只是地址翻译器——它还通过页表条目同步输出访问权限、内存类型和 Cache 策略。整个翻译过程中从 VA 到 PA 的路径完全由硬件自动完成,软件只负责维护内存中的页表数据结构。

  2. 两级页表架构是控制粒度与存储开销的平衡点:1MB Section 映射一次 L1 查表即完成,适合大块连续区域;4KB/64KB L2 页面适合精细控制的用户空间映射。短描述符格式最大支持 32-bit 物理地址,两级翻译表架构。

  3. TLB 是两级的:Micro-TLB(各 8 条目,0 周期命中)+ Main TLB(~64 条目)。无效条目永不进入 TLB,因此只修改有效条目才需 invalidate。Speculative access 也会触发页表遍历,必须在修改有效条目后做 TLB invalidation。

  4. TEX/C/B 三位一体编码决定了内存行为:Normal/Device/Strongly-ordered 三种互斥类型通过同一个 5-bit 组合编码。TEX[2]=1 时支持 Inner/Outer Cache 策略分离。

  5. ASID 是多任务 MMU 的核心创新:nG=1 的页面在 TLB 中附带 ASID 存储,上下文切换时只需写 TTBR0 + ASID 寄存器,无需刷 TLB。这比没有 ASID 时每次切换都刷 TLB 节省了巨大的 cache warming 开销。

  6. TTBR0/TTBR1 双基址寄存器分离了用户态和内核态页表:用户空间映射只需维护少量条目的瘦页表,内核映射全局共享。典型配置 N=7,32MB 用户空间 + 4GB-32MB 内核空间。

  7. Domain 机制虽已弃用但仍需正确初始化:所有页面的 Domain 字段填 0,DACR 设为 Client(0x55555555)。