1. Linux 内核内存管理概述

内存管理子系统是 Linux 内核最核心、最复杂的组件之一。它负责在有限的物理内存与无限的虚拟地址空间之间建立高效、安全的映射,并为每个进程提供独立的、私有的地址空间。理解内存管理是深入内核调试、性能优化和驱动开发的基石。

1.1 内存管理子系统的作用

内存管理子系统承担以下关键职责:

  • 地址空间隔离:确保进程 A 无法访问进程 B 的内存数据,提供硬件级保护。
  • 虚拟化抽象:让每个进程看到连续的、从 0 开始的虚拟地址空间,简化应用开发。
  • 物理内存分配与回收:管理物理页框的分配、释放、回收和交换。
  • 内存映射:支持文件映射、匿名映射、共享内存等机制。
  • 缺页异常处理:按需加载数据,实现惰性分配和写时复制。
  • 缓存与缓冲区管理:协调页缓存、slab 分配器和 DMA 缓冲区。
核心观点:内存管理子系统是进程与硬件之间的“契约管理者”,它决定了系统的稳定性、安全性和性能上限。

1.2 虚拟内存与物理内存

现代操作系统普遍采用虚拟内存技术。虚拟内存是进程看到的逻辑地址空间,物理内存是硬件 RAM 的真实存储单元。两者通过页表进行动态转换。

特性 虚拟内存 物理内存
地址空间大小 取决于 CPU 架构(如 x86_64 为 128 TB) 取决于硬件安装容量(如 8 GB、64 GB)
连续性 对进程呈现连续线性空间 物理页框可能不连续
访问权限 通过页表项控制读/写/执行 由 MMU 和页表联合控制
生命周期 随进程创建和销毁 系统启动后持续存在
交换能力 可被换出到磁盘交换分区 物理页框不可换出

虚拟内存带来的核心优势包括:

  • 进程隔离:每个进程拥有独立的页表,天然防止越界访问。
  • 简化链接与加载:所有程序可以假设从固定地址开始运行。
  • 按需分页:只有真正访问的页面才占用物理内存。
  • 共享内存:多个进程的虚拟页可以映射到同一物理页框。
关键概念:虚拟地址到物理地址的转换由 MMU(内存管理单元) 硬件完成,内核只负责维护页表结构。转换失败会触发缺页异常,由内核缺页异常处理程序接管。

1.3 内存管理核心组件

Linux 内核内存管理子系统由多个协同工作的组件构成。下图展示了核心组件及其关系:

Linux 内核内存管理核心组件架构 用户态进程 / 系统调用接口 内存描述符 (mm_struct) / 进程地址空间管理 VMA 管理 vm_area_struct 链表/红黑树 页表管理 四级页表 / 缺页异常 物理内存分配 伙伴系统 / slab 分配器 VMA 操作 mmap / munmap / mprotect MMU 硬件交互 TLB 刷新 / 页表遍历 页框管理 page 结构体 / 空闲链表 内核服务层:文件映射 / 共享内存 / 设备 DMA / 交换守护进程 说明:每个组件都通过内存描述符 mm_struct 串联,缺页异常是连接虚拟与物理的关键事件。

各核心组件的职责简述如下:

组件 核心数据结构 主要职责
内存描述符 struct mm_struct 管理进程的整个地址空间,包含页表基址、VMA 链表、代码段/数据段/堆/栈边界等。
VMA 管理 struct vm_area_struct 描述地址空间中的连续区域,记录起始地址、长度、权限、映射文件等。
页表管理 PGD/P4D/PUD/PMD/PTE 实现虚拟地址到物理地址的分级转换,处理缺页异常和权限检查。
物理内存分配 伙伴系统 + slab 管理物理页框的分配与回收,提供通用分配器和专用缓存。
页框回收 LRU 链表 + kswapd 在内存压力下回收页面,支持换出和脏页回写。
内存映射 struct file + address_space 将文件内容映射到进程地址空间,实现文件 I/O 与内存访问的统一。

1.4 内存描述符的核心地位

在 Linux 内核中,struct mm_struct 是进程地址空间的“总指挥”。每个进程(或线程组)拥有一个 mm_struct 实例,它串联起所有内存管理相关的数据结构。

关键字段示例:

struct mm_struct {
    struct vm_area_struct *mmap;        /* VMA 链表头 */
    struct rb_root mm_rb;               /* VMA 红黑树根 */
    unsigned long start_code, end_code; /* 代码段范围 */
    unsigned long start_data, end_data; /* 数据段范围 */
    unsigned long start_brk, brk;       /* 堆起始与当前边界 */
    unsigned long start_stack;          /* 栈起始地址 */
    pgd_t *pgd;                         /* 页全局目录指针 */
    atomic_t mm_users;                  /* 共享该地址空间的用户数 */
    atomic_t mm_count;                  /* 内存描述符引用计数 */
    int map_count;                      /* VMA 数量 */
    spinlock_t page_table_lock;         /* 页表锁 */
    struct rw_semaphore mmap_sem;       /* VMA 读写信号量 */
};
注意:内核线程没有独立的 mm_struct,它们直接借用上一个用户态进程的地址空间(active_mm)。这是内核线程与用户进程在内存管理上的关键区别。

1.5 本章小结