4. ARM内存管理:MMU工作原理、页表结构(一级/二级)、TLB与Cache、内存属性与域
内存管理单元(MMU),说白了就是CPU里的一个硬件模块,负责把虚拟地址翻译成物理地址。很多刚入行的朋友觉得MMU就是个地址转换器,其实远不止这么简单。它还要管权限检查、缓存策略、内存属性这些事。
我个人习惯把MMU比作一个“翻译官+保安”。翻译官负责把程序看到的虚拟地址,转成真实的物理地址;保安则检查这个访问是否合法,权限够不够。两者缺一不可。
4.1 MMU工作原理
ARM的MMU核心流程其实不复杂。CPU发出虚拟地址后,MMU会做这几件事:
- 从页表基址寄存器(TTBR)拿到页表基地址
- 根据虚拟地址的索引,逐级查找页表项
- 拿到物理地址和访问权限
- 检查权限是否匹配
- 把物理地址发给Cache或内存
我在项目中遇到过一个问题:某次调试时发现程序跑着跑着就挂了,查了半天才发现是TTBR寄存器被意外修改了。嗯,多核环境下共享页表时,一定要小心这个寄存器。
关键点:MMU不参与数据搬移,它只做地址翻译和权限检查。真正的数据读写,还是由CPU的Load/Store指令完成。
4.2 页表结构:一级与二级
ARM的页表结构,我建议你从一级页表开始理解。一级页表也叫L1页表,它把4GB地址空间分成4096个1MB的段。每个段对应一个页表项,占用4字节。所以一级页表总共16KB。
一级页表的优点是简单,查一次就能拿到物理地址。但缺点也很明显——粒度太粗,1MB一个段,浪费内存。你想想看,如果只用了几个小数组,也得占1MB的连续物理空间。
二级页表就灵活多了。它把1MB的段再细分成256个4KB的页(或者64个64KB的大页)。二级页表项也是4字节,所以每个二级页表占1KB或256字节。
| 页表级别 | 粒度 | 页表大小 | 适用场景 |
|---|---|---|---|
| 一级(L1) | 1MB段 | 16KB | 大块连续内存,如内核空间 |
| 二级(L2) | 4KB页 / 64KB大页 | 1KB / 256B | 用户空间,细粒度管理 |
我曾经在一个嵌入式项目里,为了省内存,只用了二级页表。结果发现性能不行,因为每次TLB miss都要查两次页表。后来改成一级+二级混合模式,内核空间用一级,用户空间用二级,效果好了很多。
避坑指南:一级页表项里有个“Domain”字段,ARM用这个字段做权限控制。我刚开始做驱动时,忘了设置Domain,结果所有内存访问都报错。Domain的值必须和域访问控制寄存器(DACR)配合使用。
4.3 TLB与Cache
TLB(Translation Lookaside Buffer)是页表的硬件缓存。每次地址翻译都要查页表,那太慢了。TLB把最近用过的页表项存起来,下次直接命中。
TLB和Cache的关系,我打个比方:TLB是“地址翻译的缓存”,Cache是“数据内容的缓存”。两者独立工作,但互相影响。
为什么这么说?因为Cache用的是物理地址(VIPT模式除外)。如果TLB miss了,你得先查页表拿到物理地址,然后才能去Cache里找数据。所以TLB miss的代价很高。
我记得有一次做性能优化,发现程序频繁TLB miss。查了一下,是因为进程切换太频繁,每次切换都要刷TLB。后来用了ASID(Address Space ID)机制,TLB可以保留多个进程的映射,切换时不用全刷,性能提升明显。
注意:TLB和Cache都有“一致性”问题。如果你修改了页表,必须使对应的TLB条目失效。否则MMU可能还在用旧的翻译结果。ARM提供了TLBI指令来做这件事。
4.4 内存属性与域
ARM的内存属性,说白了就是告诉MMU和Cache怎么对待某块内存。常见的属性有:
- Cacheable:是否允许Cache缓存
- Bufferable:是否允许写缓冲
- Shareable:是否在多核间共享
- Execute Never (XN):是否禁止执行代码
这些属性在页表项里设置。我建议你记住一个原则:设备内存(比如寄存器)不要设Cacheable,否则读到的可能是旧数据。普通内存可以设Cacheable,提高性能。
域(Domain)是ARM特有的权限管理机制。ARM有16个域,每个域可以设置三种访问权限:
| 域权限 | 含义 | 典型用途 |
|---|---|---|
| 00(No Access) | 禁止访问 | 未使用的内存区域 |
| 01(Client) | 按页表权限检查 | 用户空间 |
| 11(Manager) | 不检查权限,直接通过 | 内核空间 |
我曾经在调试一个安全相关的驱动时,发现用户态程序能访问内核内存。查了半天,原来是Domain设成了Manager,跳过了权限检查。这个坑,我建议你写驱动时一定要检查Domain配置。
核心总结:MMU、页表、TLB、Cache、内存属性、域,这六个概念是ARM内存管理的基石。理解它们的关系,你就能驾驭ARM的内存子系统。
4.5 知识结构图
下面这张图展示了本章的核心知识体系,我建议你对照着看:
这张图把MMU、页表、TLB、Cache、内存属性、域的关系画清楚了。你写驱动时,可以对照这张图检查自己的配置是否完整。
个人建议:刚开始学ARM内存管理,不要试图一次搞懂所有细节。先理解MMU怎么翻译地址,再学页表结构,最后看TLB和Cache。一步一步来,反而更快。