2. Linux 内存子系统概述
Linux 内存管理是内核最复杂、最核心的子系统之一。它负责在有限的物理内存与无限的虚拟地址空间之间建立高效、安全的映射,并确保内存资源在进程间公平分配、在压力下有序回收。理解物理内存、虚拟内存、页框、页表、内存映射及回收机制,是后续配置 sysctl 参数、控制 cache 压力的基础。
2.1 物理内存与虚拟内存
物理内存(Physical Memory)是硬件实际提供的 RAM 芯片,以字节为单位编址。内核通过 页框(Page Frame)将物理内存划分为固定大小的块(通常为 4KB)。
虚拟内存(Virtual Memory)是每个进程独立拥有的逻辑地址空间。它通过 页表(Page Table)将虚拟地址动态转换为物理地址。这种设计带来三大优势:
- 隔离性:进程无法直接访问其他进程或内核的物理内存。
- 简化编程:每个进程拥有连续的 32 位或 64 位地址空间,无需关心物理碎片。
- 按需分配:只有真正访问的虚拟页才会被映射到物理页框,节省内存。
关键指标:物理内存总量、虚拟内存总量、页框大小(通常 4KB,大页可配置为 2MB/1GB)。
2.2 页框与页表
页框是物理内存的最小分配单元。内核维护一个 伙伴系统(Buddy System)来管理空闲页框,支持按 2 的幂次合并与拆分。
页表是虚拟地址到物理地址的转换表。在 x86-64 架构下,采用 4 级页表(PGD、P4D、PUD、PMD、PTE)实现多级查找,减少页表本身占用的内存。
| 层级 | 全称 | 作用 |
|---|---|---|
| PGD | Page Global Directory | 顶级目录,指向下一级 |
| P4D | Page 4th Directory | 4 级目录(x86-64 可选) |
| PUD | Page Upper Directory | 上层目录 |
| PMD | Page Middle Directory | 中间目录 |
| PTE | Page Table Entry | 页表项,直接映射到页框 |
性能提示:TLB(Translation Lookaside Buffer)是页表的硬件缓存。大页(Huge Pages)可以减少页表项数量,提升 TLB 命中率,对数据库、HPC 场景尤其重要。
2.3 内存映射
内存映射(Memory Mapping)是 Linux 实现文件 I/O 和进程间共享内存的核心机制。通过 mmap() 系统调用,可以将文件或设备的一部分直接映射到进程的虚拟地址空间。
- 文件映射:将磁盘文件内容映射到内存,读写内存即读写文件,减少
read()/write()系统调用开销。 - 匿名映射:不关联文件,用于进程堆、栈的扩展,或通过
MAP_SHARED实现父子进程共享内存。 - 页缓存(Page Cache):内核自动将磁盘数据缓存在物理页框中,所有文件 I/O 都经过页缓存,大幅提升性能。
注意:页缓存是 cache 压力的主要来源。当物理内存不足时,内核必须回收页缓存,可能导致 I/O 抖动。通过
sysctl 参数(如 vm.vfs_cache_pressure)可以控制回收倾向。
2.4 内存回收机制
当系统物理内存紧张时,内核启动 内存回收(Reclaim)机制,释放可回收页框。主要回收对象包括:
| 回收对象 | 说明 | 回收代价 |
|---|---|---|
| 页缓存(Page Cache) | 文件数据的缓存页 | 低(脏页需写回磁盘) |
| slab 缓存 | 内核对象缓存(如 inode、dentry) | 中 |
| 匿名页(Anonymous Pages) | 进程堆、栈、匿名映射页 | 高(需换出到 swap) |
回收触发方式分为两种:
- 直接回收(Direct Reclaim):进程在分配内存时发现空闲页不足,直接参与回收,导致延迟抖动。
- 后台回收(kswapd):内核线程
kswapd在后台异步扫描 LRU 链表,提前回收页框,避免直接回收。
2.5 关键数据结构:LRU 链表
内核使用 LRU(Least Recently Used) 链表管理可回收页框。每个内存区域(zone)维护两组链表:
- 活跃链表(active list):存放近期被访问过的页框。
- 非活跃链表(inactive list):存放长时间未被访问的页框,优先回收。
内核通过 kswapd 或直接回收时,扫描非活跃链表,将符合条件的页框回收。若非活跃链表不足,会从活跃链表尾部移动部分页框到非活跃链表头部。
调优关联:
vm.swappiness 参数控制内核倾向于回收匿名页还是页缓存。值越小(如 10),越优先回收页缓存;值越大(如 100),越积极使用 swap 换出匿名页。