1. MMU概述:为什么需要MMU?
各位同学,咱们今天聊聊MMU。说实话,我刚入行那会儿,觉得MMU就是个可有可无的东西——不就是把地址转来转去嘛,多此一举。直到有一次,我在一个嵌入式项目里,两个任务同时访问了同一块物理内存,结果数据乱得一塌糊涂。从那以后,我才真正理解了MMU的价值。
1.1 为什么需要MMU?
你想想看,如果没有MMU,程序直接操作物理地址,会是什么场景?
- 多个程序打架:两个程序同时写0x1000这个地址,谁先写谁后写?数据全乱套。
- 内存不够用:一个程序需要2GB内存,但物理内存只有1GB,怎么办?
- 权限形同虚设:任何程序都能读写任何地址,恶意代码轻松搞破坏。
MMU就是来解决这些问题的。它给每个程序一个「假想世界」——虚拟地址空间。每个程序都觉得自己独占整个内存,互不干扰。
核心思想:MMU让每个进程拥有独立的虚拟地址空间,通过地址翻译实现隔离和保护。
1.2 虚拟地址与物理地址的概念
这两个概念,我习惯用一个比喻来解释。
物理地址:就是内存芯片上真实的地址线。比如你的板子有512MB DDR3,物理地址范围就是0x00000000到0x1FFFFFFF。这是硬件决定的,改不了。
虚拟地址:是CPU发出的地址。程序里写的指针、变量地址,全是虚拟地址。每个进程都有自己的虚拟地址空间,比如32位系统是0到4GB。
MMU干的事,就是把虚拟地址翻译成物理地址。这个过程叫「地址翻译」或「地址转换」。
我个人经验:调试时经常遇到「段错误」,十有八九是虚拟地址没映射好。记住一点——CPU永远看不到物理地址,它只认虚拟地址。
1.3 MMU的核心功能
MMU不是只会做地址翻译,它还有三个看家本领。
1.3.1 地址翻译
这是MMU最基础的功能。CPU发出虚拟地址,MMU查页表,找到对应的物理地址。
举个例子,ARMv8-A架构下,页表查找过程是这样的:
// 虚拟地址拆分(以4KB页、4级页表为例)
// 假设虚拟地址:0x7F8A_3B2C
// 第0级索引:VA[47:39] -> 9位
// 第1级索引:VA[38:30] -> 9位
// 第2级索引:VA[29:21] -> 9位
// 第3级索引:VA[20:12] -> 9位
// 页内偏移: VA[11:0] -> 12位
// 翻译过程:
// 1. 从TTBR0_EL1寄存器拿到页表基地址
// 2. 用第0级索引找到下一级页表的地址
// 3. 逐级查找,直到第3级页表
// 4. 第3级页表项里存的就是物理页帧号
// 5. 物理地址 = 物理页帧号 + 页内偏移
嗯,这里要注意,每次地址翻译都要查4次页表,性能开销不小。所以MMU里有个TLB(快表),缓存最近用过的翻译结果。
避坑指南:我曾经在一个项目里,频繁切换进程导致TLB不断刷新,性能直接掉了30%。后来用了ASID(地址空间标识符)才解决。记住,TLB miss是性能杀手。
1.3.2 权限控制
MMU不光管地址翻译,还管「谁可以访问、怎么访问」。
每个页表项里都有一堆权限位:
| 权限位 | 含义 | 我常用的场景 |
|---|---|---|
| AP[2:1] | 访问权限(读/写/执行) | 代码段设为只读+可执行,数据段设为读写 |
| XN | 禁止执行 | 栈和堆绝对不能执行代码,防溢出攻击 |
| PXN | 特权模式禁止执行 | 用户态代码不能执行内核地址 |
| AF | 访问标志位 | 用于页面置换算法,判断页面是否被访问过 |
说白了,权限控制就是给内存上了三把锁——读锁、写锁、执行锁。哪个程序能开哪把锁,MMU说了算。
实际案例:我在做安全启动时,把关键配置寄存器映射到虚拟地址后,直接设成「特权模式只读」。就算用户态程序拿到物理地址,也改不了配置。这就是MMU权限控制的威力。
1.3.3 缓存管理
MMU还管缓存?没错。它决定了哪些内存区域可以被缓存,以及怎么缓存。
页表项里有几个关键属性:
- Shareability:是否被多个核共享。共享内存要设成「共享」,否则缓存一致性会出问题。
- Cacheability:是否允许缓存。外设寄存器通常要设成「非缓存」,否则读到的是旧数据。
- 内存类型:Normal(普通内存)还是Device(设备内存)。设备内存必须严格按顺序访问。
你想想看,如果DMA和外设共享一块内存,MMU却把这块内存缓存了,DMA写完后CPU读到的还是缓存里的旧数据。这就是经典的缓存一致性问题。
我的习惯:做驱动开发时,外设寄存器一律映射为「非缓存、非推测、严格顺序」。虽然性能差点,但不会出诡异bug。等调通了再考虑优化。
1.4 小结
MMU说白了就三件事:
- 地址翻译:虚拟地址→物理地址,让每个进程觉得独占内存
- 权限控制:读/写/执行权限,保护系统不被恶意破坏
- 缓存管理:控制缓存策略,保证数据一致性
没有MMU,现代操作系统根本跑不起来。Linux的进程隔离、内存保护、虚拟内存,全建立在MMU之上。下一章咱们深入ARM的MMU实现,看看它和RISC-V到底有什么不同。
一句话总结:MMU是现代操作系统的基石,理解它,你就理解了整个内存管理体系的灵魂。