内存分配故障排查 内核工程OOM与内存泄漏分析 - 第1节

1. 内存管理基础

物理内存(Physical Memory)是系统中实际存在的硬件资源,通常由 DRAM 芯片组成,以字节为单位编址。每个物理地址(Physical Address, PA)对应一个具体的存储单元。然而,现代操作系统几乎不直接向应用程序暴露物理地址,而是通过虚拟内存(Virtual Memory)机制为每个进程提供独立的、连续的地址空间。

核心概念对比
维度 物理内存 虚拟内存
本质 硬件 DRAM 芯片上的实际存储单元 操作系统为进程抽象的逻辑地址空间
地址空间 由硬件总线宽度决定(如 48 位物理地址) 由处理器架构决定(如 x86-64 为 48 位虚拟地址)
可见性 仅内核和 MMU 可见 每个用户进程独立拥有,互不干扰
连续性 物理上可能不连续(存在碎片) 对进程表现为连续的线性空间
容量限制 受限于硬件插槽和主板支持 可超过物理内存(通过交换分区/文件)

虚拟内存的核心价值在于:隔离(进程间互不干扰)、简化(程序员无需管理物理碎片)、按需分配(仅实际访问时才分配物理页框)。

虚拟内存与物理内存映射关系 进程 A 虚拟地址空间 0x0000 0000 代码段 数据段 堆 栈 0x7fff ffff MMU 页表转换 物理内存(DRAM) 页框 0 页框 1 页框 2 页框 3 页框 4 页框 5 页框 6 页框 7 关键点 虚拟地址连续,物理地址可以不连续 未访问的虚拟页可以不分配物理页框
面试高频题:为什么 32 位系统最大支持 4GB 内存?因为 32 位虚拟地址空间为 2^32 = 4GB。但物理内存可以通过 PAE(Physical Address Extension)扩展到 64GB,这正体现了虚拟地址与物理地址的解耦。

1.2 MMU 与页表:地址转换的核心引擎

内存管理单元(Memory Management Unit, MMU)是 CPU 内部负责虚拟地址到物理地址转换的硬件模块。页表(Page Table)是 MMU 工作的核心数据结构,它记录了虚拟页号(VPN)到物理页框号(PFN)的映射关系。

1.2.1 地址转换流程

  1. CPU 发出虚拟地址:指令指针或访存指令生成一个 48 位虚拟地址。
  2. MMU 解析页号与偏移:虚拟地址被拆分为虚拟页号(VPN)和页内偏移(Offset)。
  3. 页表遍历(Page Walk):MMU 从页表基址寄存器(CR3)出发,逐级查找页表项(PTE)。
  4. TLB 加速:如果转换后备缓冲器(TLB)命中,则跳过页表遍历,直接获得物理地址。
  5. 物理地址访问:MMU 将转换后的物理地址发送到内存控制器,完成数据读写。
页表项(PTE)关键标志位
标志位 含义 故障排查意义
Present (P) 该页是否在物理内存中 P=0 时访问触发缺页异常(Page Fault)
Read/Write (R/W) 是否允许写入 写只读页触发保护异常
User/Supervisor (U/S) 用户态是否可访问 用户态访问内核页触发异常
Accessed (A) 该页是否被访问过 内核利用此位实现页面置换算法
Dirty (D) 该页是否被写入过 决定换出时是否需要写回磁盘
MMU 地址转换流程(四级页表示例) 虚拟地址 (48-bit) [PML4:9bit] [PDPT:9bit] [PD:9bit] [PT:9bit] [Offset:12bit] PML4 Level 4 PDPT Level 3 PD Level 2 PT Level 1 物理地址 (48-bit) [物理页框号:36bit] [Offset:12bit] TLB 缓存:命中时直接跳过页表遍历,大幅降低地址转换延迟 CR3 寄存器指向当前进程的 PML4 基址,进程切换时 CR3 更新
OOM 排查关联:当系统物理内存不足时,内核会尝试回收页框。如果页表项中的 Accessed 位长期为 0,说明该页最近未被使用,内核会优先将其换出。理解页表标志位有助于分析内存回收策略是否生效。

1.3 内核空间与用户空间:权限隔离与地址划分

在 x86-64 Linux 系统中,虚拟地址空间被划分为两个主要区域:用户空间(User Space)和内核空间(Kernel Space)。典型的划分比例为 47:17(用户空间占低 47 位地址,内核空间占高 47 位地址),中间留有空隙用于捕获越界访问。

1.3.1 地址空间布局

区域 地址范围(x86-64 典型) 权限 内容
用户空间 0x0000 0000 0000 0000 ~ 0x0000 7FFF FFFF FFFF Ring 3(用户态) 进程代码、数据、堆、栈、共享库
内核空间 0xFFFF 8000 0000 0000 ~ 0xFFFF FFFF FFFF FFFF Ring 0(内核态) 内核代码、模块、直接映射区、vmalloc、固定映射

1.3.2 内核空间内部细分

  • 直接映射区(Direct Mapping / Linear Mapping):物理内存直接映射到内核虚拟地址空间,偏移量固定(如 PAGE_OFFSET)。内核通过简单的加减偏移即可访问任意物理页。
  • vmalloc 区:用于分配虚拟地址连续但物理地址不连续的内存,适用于大块内存分配。
  • 固定映射区(Fixmap):用于需要固定虚拟地址的场合,如 APIC 寄存器映射。
  • 模块区:用于加载内核模块(.ko 文件)。
x86-64 Linux 虚拟地址空间划分 用户空间 (User Space) 0x0000 0000 0000 0000 ~ 0x0000 7FFF FFFF FFFF 代码段 | 数据段 | 堆 | 栈 | 共享库 | vsyscall 权限:Ring 3(用户态) 非规范地址区域(捕获越界访问) 内核空间 (Kernel Space) 0xFFFF 8000 0000 0000 ~ 0xFFFF FFFF FFFF FFFF 权限:Ring 0(内核态) 直接映射区 vmalloc 区 固定映射区 模块区 用户态通过系统调用(syscall)陷入内核态,切换栈和页表基址
故障排查要点:OOM 发生时,内核空间的内存分配(如 slab、vmalloc)也可能成为瓶颈。通过 /proc/meminfo 中的 Slab、VmallocUsed 字段可以快速定位内核内存泄漏。

1.4 本章知识结构总结

知识点 核心内容 与 OOM/内存泄漏的关联
物理内存 硬件 DRAM,以页框为单位管理 物理内存耗尽触发 OOM Killer
虚拟内存 进程独立地址空间,按需分配物理页 虚拟地址空间耗尽(如 32 位系统)也可能导致分配失败
MMU 硬件地址转换单元,依赖页表 页表分配失败会导致内核 panic
页表 多级映射结构,PTE 包含权限和状态位 页表项异常(如 Dirty 位错误)可导致内存泄漏误判
内核空间 Ring 0 特权区域,直接映射物理内存 内核内存泄漏(如 slab 未释放)是 OOM 的常见原因
用户空间 Ring 3 非特权区域,进程隔离 用户态内存泄漏最终导致进程被 OOM Killer 终止