1. 为什么需要内存管理
早期系统中,程序直接使用物理地址运行,问题很明显:
- 进程之间缺少隔离
- 内存利用率低
- 多任务切换和扩展性差
现代操作系统通过虚拟内存机制解决这些问题,让进程看到的是各自独立的地址空间,而不是直接操作物理内存。
2. 从分段到分页
分段
分段把程序划分为代码段、数据段、栈段等逻辑区域,通过地址映射实现隔离。
优点:
- 有助于按逻辑划分程序
- 比直接使用物理地址更安全
局限:
- 仍然不够灵活
- 对内存装载和交换不够友好
分页
分页进一步把地址空间切成固定大小的页,通过页表建立虚拟地址到物理地址的映射。
优点:
- 支持按页分配
- 支持按需加载
- 更适合缺页、换页和内存回收机制
3. 虚拟地址、物理地址和 MMU
应用程序访问的是虚拟地址。MMU 根据页表把虚拟地址转换为物理地址,再访问真实内存。
这一过程的几个关键点:
- 页表保存映射关系
- TLB 缓存常用地址转换结果
- TLB 命中时转换更快
- TLB miss 时需要重新查页表
因此,内存管理不仅是软件问题,也和 MMU、TLB、Cache 等硬件能力密切相关。
4. 进程视角下的内存管理
每个进程都有自己的虚拟地址空间。Linux 使用 mm_struct 和 vm_area_struct 来描述这些区域。
常见区域包括:
- 代码段
- 数据段
- 堆
- 栈
mmap区域
vm_area_struct 用于管理一段连续的虚拟地址区域,不同区域可能具有不同的权限、映射方式和用途。
5. 缺页中断
缺页中断体现了“按需分配”的思想。进程申请到一段虚拟地址时,并不代表物理页已经立即分配。真正访问到该地址时,如果页表中还没有对应映射,就会触发缺页中断,由内核决定:
- 分配匿名页
- 建立文件页缓存映射
- 或者从磁盘 / swap 中把页面换入
6. 物理页管理
伙伴系统
伙伴系统以页为单位管理物理内存,适合较大块内存分配。其思路是:
- 内存按 2 的幂次划分
- 不够小时拆分
- 释放时尝试和伙伴页合并
slab 分配器
当内核只需要较小对象时,直接按页分配成本太高。此时通常由 slab 分配器负责管理固定大小的小对象,提高分配效率并减少碎片。
7. 页面回收与 swap
当系统内存不足时,Linux 会尝试回收不活跃页面。回收对象主要包括:
- page cache
- 匿名页
page cache 中的干净页回收成本较低;匿名页一般需要换出到 swap,后续访问时再换入。
8. 其他关键机制
反向映射
反向映射用于从物理页反查哪些虚拟地址映射到了它,常用于页面回收和映射解除。
huge page
大页可以减少 TLB miss,适合对性能敏感、内存较大的场景。
内存规整
内存规整用于缓解长期运行后的物理碎片问题,尽量腾出更大的连续物理块。
OOM
当回收、规整等手段都无法满足分配请求时,系统可能触发 OOM killer,杀掉部分进程释放内存。
9. 理解 Linux 内存管理的推荐主线
如果要建立一个比较稳的理解框架,建议按下面的顺序学习:
- 虚拟地址和物理地址
- 分页和页表
- 进程地址空间与 VMA
- 缺页中断
- 伙伴系统与 slab
- 页面回收、swap、OOM
10. 总结
Linux 内存管理的本质,是在有限物理内存上为进程提供抽象、隔离和弹性的地址空间。它同时依赖:
- 硬件:MMU、TLB、Cache
- 数据结构:页表、VMA、
struct page、zone - 内核机制:缺页中断、伙伴系统、slab、回收、swap、OOM
把这些点串起来之后,再看具体内核实现,会更容易建立整体感。