1. MMU基础概念:什么是MMU、为什么需要MMU、MMU在系统中的位置
各位同学,咱们今天聊聊MMU。说实话,我刚开始接触嵌入式Linux时,对MMU也是一头雾水。那时候我还在做裸机开发,压根不知道这玩意儿有什么用。直到有一次,我在一个项目中遇到了内存碎片问题,折腾了好几天...嗯,从那以后我才真正理解了MMU的价值。
什么是MMU?
MMU,全称Memory Management Unit,中文叫内存管理单元。说白了,它就是个硬件模块,负责把程序看到的地址(虚拟地址)转换成物理内存上真实的地址(物理地址)。
你可以把它想象成一个翻译官。程序说“我要访问地址0x1000”,MMU就会查一下自己的小本本(页表),然后告诉你:“哦,这个地址对应的物理地址是0x8000,你去那儿找吧。”
核心概念:MMU是现代操作系统中实现虚拟内存的关键硬件单元。没有它,Linux的多进程、内存保护、共享内存这些特性都玩不转。
为什么需要MMU?
这个问题我经常被问到。你想想看,如果没有MMU,会是什么样子?
- 地址空间冲突:每个程序都直接操作物理地址,A程序用了0x1000,B程序就不能用了。多进程?想都别想。
- 内存碎片:程序申请释放内存,物理内存会变得支离破碎。我在一个老项目里就吃过这个亏,连续分配大块内存时总是失败。
- 安全性:没有MMU,任何程序都能访问任何物理地址。恶意程序可以随便篡改内核数据,想想都可怕。
MMU解决了这三个核心问题:
- 隔离性:每个进程有自己的虚拟地址空间,互不干扰
- 连续性:虚拟地址是连续的,物理地址可以是不连续的碎片
- 保护性:通过页表权限位,控制哪些地址可读、可写、可执行
我个人习惯:在调试内存问题时,第一件事就是检查MMU的页表配置。很多诡异的内存访问错误,追根溯源都是页表设置不对。
MMU在系统中的位置
MMU位于CPU核心和物理内存之间。具体来说,它挂在CPU的存储总线上,所有内存访问请求都要经过它。
下面这张图是我手绘的,展示了MMU在系统中的位置:
从图中可以看到,MMU夹在CPU和内存之间。每次CPU访问内存,都要经过MMU这道关卡。这听起来好像会拖慢速度,对吧?
嗯,确实会有一些性能开销。但现代MMU都内置了TLB(Translation Lookaside Buffer),也就是页表缓存。它会把最近用过的地址转换结果存起来,下次直接命中,速度飞快。我在调试一个视频播放器时,就遇到过TLB miss率过高导致卡顿的问题,后来优化了内存访问模式才解决。
MMU的工作流程
简单来说,MMU做三件事:
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 接收虚拟地址 | CPU发出VA(虚拟地址),MMU截获 |
| 2 | 查询页表 | MMU根据VA的高位索引页表,找到对应的物理页框号 |
| 3 | 权限检查 | 检查访问类型(读/写/执行)是否被允许 |
| 4 | 生成物理地址 | 将物理页框号与VA的低位偏移组合,得到PA |
| 5 | 访问内存 | 用PA去物理内存中读写数据 |
我曾经踩过的坑:在移植Linux内核到新平台时,忘了配置MMU的页表基地址寄存器。结果内核一启动就崩溃,因为CPU发出的所有地址都没有经过正确转换。调试了整整两天才发现是这个问题。所以,大家做底层开发时,一定要先确认MMU的初始化代码是否正确。
MMU与SMMU的关系
说到MMU,就不得不提SMMU(System Memory Management Unit)。SMMU是给外设用的MMU,比如GPU、网卡、存储控制器等。它们也需要访问内存,也需要地址转换和保护。
我个人的理解是:MMU管CPU,SMMU管外设。两者原理相似,但应用场景不同。在后面的章节中,我们会详细讲解SMMU的驱动开发。
一个小技巧:在调试MMU相关问题时,可以查看/proc/cpuinfo和/proc/meminfo。前者能告诉你CPU是否支持MMU,后者能显示虚拟内存的使用情况。我经常用这两个文件快速定位问题。
好了,关于MMU的基础概念就讲到这里。记住三个关键词:地址转换、内存保护、虚拟内存。理解了这些,后面的内容就好办了。
公众号:蓝海资料掘金营,微信deep3321