4、内存子系统概览:Linux内存管理架构
内存调优,说白了就是跟数据搬家和地址翻译打交道。我做了这么多年BSP,最深的体会是:不懂内存管理,性能调优就是瞎蒙。这一章咱们把Linux内存管理的骨架搭起来,后面再聊具体怎么调。
4.1 从CPU到物理内存:MMU在中间干了什么
CPU发出的地址,其实不是直接去找物理内存的。中间有个叫MMU(Memory Management Unit)的硬件模块,它负责把虚拟地址翻译成物理地址。你想想看,如果每个进程都直接操作物理地址,那系统早就乱套了。
MMU的核心工作就三件事:
- 地址翻译:把虚拟地址转成物理地址
- 权限检查:判断当前操作是否合法(读/写/执行)
- 缓存控制:管理TLB(后面会细说)
我在项目中遇到过一个问题:某款ARM芯片跑Linux,用户态程序访问一个合法的虚拟地址,结果触发段错误。查了半天,发现是MMU的页表项里权限位配错了。嗯,这种坑,硬件手册不看仔细就容易栽。
4.2 页表:虚拟地址到物理地址的映射表
页表就是一张映射表,记录着虚拟页号到物理页框号的对应关系。Linux用的是多级页表,不是一张大表。为什么?
假设32位系统,4KB页大小,虚拟地址空间4GB。如果只用一级页表,需要2^20个页表项,每个4字节,那就是4MB。每个进程一张表,100个进程就是400MB。这还不算完,很多页表项其实是空的——进程根本没用那么多内存。
多级页表就好多了。以ARM64的四级页表为例:
| 级别 | 名称 | 粒度 | 覆盖范围 |
|---|---|---|---|
| PGD | Page Global Directory | 512GB | 整个地址空间 |
| P4D | Page 4th Directory | 1GB | PGD下的子区域 |
| PUD | Page Upper Directory | 2MB | P4D下的子区域 |
| PMD | Page Middle Directory | 2MB | PUD下的子区域 |
| PTE | Page Table Entry | 4KB | 物理页框 |
只有真正用到的地址范围才会分配页表。这就是所谓的「按需分配」。我刚开始接触这个的时候也觉得绕,但画个图就清楚了。
关键点:多级页表节省内存,但代价是地址翻译变慢——每次访问内存都要查好几级表。所以TLB就派上用场了。
4.3 TLB:地址翻译的加速器
TLB(Translation Lookaside Buffer)是MMU内部的一个小容量高速缓存,专门缓存最近用过的页表项。说白了,就是地址翻译的「快照」。
TLB的工作原理很简单:
- CPU发出虚拟地址
- MMU先在TLB里找
- 命中:直接拿到物理地址,走人
- 未命中:查页表,更新TLB,再走
TLB命中率直接决定内存访问延迟。我调过一款网络处理器,TLB缺失率从5%降到1%,整机吞吐量提升了12%。你想想看,每次TLB缺失都要多走好几级页表,累积起来就是大开销。
实战技巧:TLB缺失率可以用perf stat -e dTLB-load-misses,iTLB-load-misses来监控。如果发现缺失率超过2%,就该考虑大页(HugePage)了。
4.4 物理内存与虚拟内存
这两个概念经常被混淆。我简单说清楚:
- 物理内存:就是板子上焊的那些DDR颗粒,实实在在的硬件资源
- 虚拟内存:每个进程看到的独立地址空间,由MMU+页表+物理内存共同实现
虚拟内存的好处很明显:
- 进程隔离:A进程崩了不影响B进程
- 地址空间连续:程序员不用管物理内存碎片
- 共享内存:多个进程可以映射到同一块物理内存
- 按需分配:用多少分配多少,不用提前占满
但虚拟内存不是免费的。每次上下文切换都要刷TLB(或者用ASID优化),页表维护也有开销。在实时性要求高的场景,这些开销必须算清楚。
我曾经在一个车载项目中,因为虚拟内存的缺页处理延迟太高,导致音频中断超时。后来改用mlock锁住关键内存页,才把问题解决。
4.5 内存碎片化:BSP工程师的噩梦
内存碎片化分两种:
| 类型 | 表现 | 原因 | 后果 |
|---|---|---|---|
| 外部碎片 | 总空闲内存够,但无法分配大块连续内存 | 频繁分配释放,空闲页分散 | 大块分配失败,OOM |
| 内部碎片 | 分配的内存块比实际需求大 | 伙伴系统最小粒度限制 | 内存利用率低 |
外部碎片是BSP工程师最头疼的。系统跑着跑着,突然分配一个16KB的缓冲区就失败了,但/proc/meminfo显示还有几百MB空闲。这就是典型的碎片化。
避坑指南:我曾经在一个视频处理项目中,连续运行72小时后,驱动申请DMA缓冲区失败。查到最后,是内核的伙伴系统无法合并出足够大的连续物理页。解决方案是:
- 启动时预留大块内存(CMA)
- 使用vmalloc分配非连续内存
- 定期触发内存压缩(compact_memory)
4.6 知识体系总览
下面这张图把本章的核心逻辑串起来了。我建议你多看几遍,把每个模块的职责和关系理清楚。
4.7 本章小结
内存管理架构是BSP性能调优的基石。我个人的经验是:
- 先搞清楚MMU和页表的工作原理,这是基础
- TLB命中率是性能瓶颈的常见来源,用perf盯住它
- 虚拟内存提供了便利,但实时场景下要小心缺页延迟
- 碎片化问题越早预防越好,别等到系统跑崩了再查