1. 内存管理基础:物理内存与虚拟内存的概念、MMU 的作用、页表与地址转换

在深入 OOM Killer 之前,必须牢固掌握 Linux 内存管理的三大基石:物理内存、虚拟内存以及连接二者的核心硬件——MMU(内存管理单元)。OOM Killer 的触发、评分与最终杀进程行为,都建立在这些底层机制之上。

1.1 物理内存 vs 虚拟内存

特性 物理内存 (Physical Memory) 虚拟内存 (Virtual Memory)
本质 硬件 RAM 芯片上的实际存储单元 操作系统为每个进程提供的抽象地址空间
地址空间 由硬件总线宽度决定(如 64 位系统最大支持 2^48 物理地址) 每个进程拥有独立的 2^64(或 2^32)虚拟地址空间
可见性 所有进程共享,由内核统一管理 每个进程认为自己是唯一占用者,地址从 0 开始
碎片问题 外部碎片严重,分配大块连续内存困难 通过页表映射,物理上可以不连续,逻辑上连续
进程隔离 无天然隔离,进程 A 可直接访问进程 B 的数据 天然隔离,进程 A 无法访问进程 B 的虚拟地址
关键理解:OOM Killer 监控的是物理内存的耗尽程度。当物理内存 + Swap 被消耗殆尽,内核无法再为虚拟内存页分配物理页框时,才会触发 OOM。虚拟内存的“超额承诺”(Overcommit)机制是导致物理内存快速耗尽的直接推手。

1.2 MMU 的作用:地址转换的硬件引擎

MMU(Memory Management Unit)是 CPU 内部的一个硬件模块,负责在指令执行阶段,将 CPU 发出的虚拟地址实时转换为物理地址。没有 MMU,虚拟内存的概念将无法实现。

MMU 地址转换核心流程 进程 A 虚拟地址 0x7fff... MMU 页表查找 + 权限检查 TLB 加速 物理内存 页框 0x1a2b3c... 页表(Page Table) 存储在物理内存中 虚拟地址 物理地址 查询页表 💡 核心作用: MMU 让每个进程拥有独立的虚拟地址空间,实现进程隔离、简化内存分配、支持按需分页。

1.3 页表与地址转换详解

页表是 MMU 进行地址转换的“字典”。Linux 在 x86-64 架构下使用 4 级页表(PGD → PUD → PMD → PTE),将虚拟地址逐级拆解,最终找到物理页框号(PFN)。

地址转换步骤(以 4KB 页为例)

  1. 拆分虚拟地址:CPU 发出的 48 位虚拟地址被拆分为 5 个部分:9 位 PGD 索引、9 位 PUD 索引、9 位 PMD 索引、9 位 PTE 索引、12 位页内偏移。
  2. 逐级查表:MMU 从进程的 PGD 基址寄存器(CR3)开始,依次查找 PGD、PUD、PMD、PTE 四级页表项。
  3. 获取物理页框:最后一级 PTE 中存储了物理页框号(PFN),将 PFN 与页内偏移拼接,得到完整的物理地址。
  4. TLB 加速:MMU 内部有一个高速缓存 TLB(Translation Lookaside Buffer),缓存最近使用的虚拟地址到物理地址的映射,避免每次转换都走一遍 4 级页表。
性能提示:TLB 未命中(TLB Miss)是内存访问延迟的主要来源之一。大页(HugePages)通过减少页表层级和 TLB 条目数,能显著降低 TLB Miss 率,对数据库、HPC 等大内存应用至关重要。

页表项(PTE)关键标志位

标志位 含义 对 OOM 的影响
Present (P) 该虚拟页是否已映射到物理页框 若为 0,访问将触发缺页异常(Page Fault),可能引发内存分配
Dirty (D) 该物理页框内容是否被修改过 OOM 发生时,脏页需要写回 Swap 或磁盘才能回收
Accessed (A) 该页是否被访问过 内核的页面回收算法(PFRA)依赖此位判断页面的活跃程度
RW 读写权限 写保护页(如代码段)不能被 OOM Killer 直接修改

1.4 虚拟内存布局与 OOM 的关联

一个典型的 Linux 进程虚拟地址空间布局如下(以 x86-64 为例):

Linux 进程虚拟地址空间布局(x86-64) 内核空间 (0xffff800000000000 - 0xffffffffffffffff) 栈 (Stack) — 向下增长 内存映射段 (mmap) — 文件映射、共享库、匿名映射 堆 (Heap) — 向上增长 (brk/sbrk) BSS 段 (未初始化全局变量) 数据段 (已初始化全局变量) 代码段 (Text) — 只读、可执行
OOM 风险区域:堆(Heap)和内存映射段(mmap)是 OOM Killer 最常“光顾”的区域。堆的无限增长(内存泄漏)或 mmap 分配大量匿名页,会快速耗尽物理内存。栈溢出通常导致段错误(Segfault),而非 OOM。

1.5 缺页异常:虚拟到物理的桥梁

当 CPU 访问一个虚拟地址,而 MMU 在页表中发现对应的 PTE 的 Present 位为 0 时,会触发缺页异常(Page Fault)。Linux 内核的缺页异常处理程序会根据情况执行不同操作:

  • 有效缺页(Major/Minor Page Fault):PTE 有效但页不在内存中。内核从 Swap 分区或磁盘文件中读取数据到物理内存,然后更新页表。这是正常的内存按需加载行为。
  • 写时复制(COW)缺页:PTE 的 Present 位为 1,但 RW 位为 0。内核复制物理页框并赋予写权限。这是 fork() 后父子进程共享内存的优化机制。
  • 无效缺页(Segmentation Fault):PTE 完全无效(如访问未映射的地址)。内核向进程发送 SIGSEGV 信号,进程崩溃。
  • OOM 触发缺页:缺页处理程序尝试分配物理页框,但此时物理内存已耗尽且 Swap 已满,分配失败。内核进入 OOM Killer 流程。
OOM 触发路径:进程访问虚拟地址 → MMU 缺页异常 → 内核缺页处理程序 → 调用 alloc_pages() 分配物理页 → 内存不足 → 调用 __alloc_pages_slowpath() → 尝试回收页面失败 → 调用 out_of_memory() → OOM Killer 选择目标进程 → 发送 SIGKILL。

1.6 本章小结

物理内存是有限的硬件资源,虚拟内存通过 MMU 和页表为每个进程提供了独立、连续、安全的地址空间。缺页异常是虚拟地址真正映射到物理页框的触发点,也是 OOM Killer 被调用的最终入口。理解这些基础概念,是后续分析 OOM Killer 评分机制、选择策略和排查内存泄漏的必备前提。