1. 内存管理概述:Linux内存管理架构、虚拟内存与物理内存、内存分配器的作用

1.1 Linux 内存管理架构全景

Linux 内核的内存管理子系统是操作系统最核心、最复杂的模块之一。它负责在有限的物理内存资源下,为进程、内核、缓存、设备等所有系统组件提供高效、安全、公平的内存分配与回收服务。理解其分层架构是掌握 Slab 分配器与内存碎片调优的前提。

下图展示了 Linux 内存管理的核心分层架构,从硬件到用户空间,每一层都承担着特定的抽象与职责:

Linux 内存管理分层架构 用户空间 (User Space) malloc / free / mmap / brk — 标准 C 库与用户态分配器 系统调用接口 (sys_mmap / sys_brk / sys_mprotect) 虚拟内存管理 (VMA / 缺页异常 / 反向映射) 管理进程地址空间,处理缺页中断,维护页表 内核核心分配器 伙伴系统 (Buddy) 物理页框管理 / 大块分配 Slab 分配器 小对象缓存 / 对象复用 页缓存 / 块层 文件页 / 匿名页 / 交换 硬件抽象层 (MMU / TLB / 页表 / 内存控制器) 物理地址转换 / 缓存一致性 / NUMA 节点 物理内存 (DRAM / NUMA 节点 / ZONE_DMA / ZONE_NORMAL / ZONE_HIGHMEM) 物理页框 / 内存节点 / 内存域
架构要点: 用户态通过系统调用陷入内核;VMA 层管理虚拟地址空间;伙伴系统管理物理页框;Slab 分配器在伙伴系统之上提供小对象缓存;页缓存层处理文件与匿名页的 I/O 优化。每一层都直接影响内存碎片与分配效率。

1.2 虚拟内存与物理内存

虚拟内存是现代操作系统内存管理的基石。每个进程拥有独立的 4GB(32位)或 128TB(64位)虚拟地址空间,通过 MMU 和页表映射到物理内存。这种抽象带来了隔离、安全、以及“大于物理内存”的错觉。

特性 虚拟内存 物理内存
地址空间 每个进程独立,连续线性 全局共享,可能不连续
大小 受 CPU 位宽限制(如 48位 = 256TB) 受硬件容量限制(如 64GB)
分配粒度 页(4KB / 2MB / 1GB) 页框(4KB 或大页)
碎片来源 VMA 碎片(地址空间空洞) 外部碎片(伙伴系统无法合并)
与 Slab 的关系 Slab 分配器不直接管理虚拟地址 Slab 从伙伴系统获取物理页,再切分为小对象

下图展示了虚拟地址到物理地址的映射过程,以及 Slab 分配器在其中扮演的角色:

虚拟地址 → 物理地址映射与 Slab 分配器位置 进程虚拟地址空间 栈 (Stack) 堆 (Heap / brk) BSS / 数据段 代码段 ... mmap 区域 内核空间 (高地址) 页表 (Page Table) PGD → PUD → PMD → PTE 虚拟页号 → 物理页框号 权限 / 存在位 / 脏位 TLB 缓存加速 物理内存 ZONE_DMA (0-16MB) ZONE_NORMAL (16MB-896MB) ZONE_HIGHMEM (>896MB) Slab 分配器 从伙伴系统获取页 → 切分为小对象
关键理解: Slab 分配器并不直接操作虚拟地址或页表。它从伙伴系统获取连续的物理页框(通常为 2^order 个页),然后在页框内部进行细粒度划分,为内核对象(如 inode、task_struct、dentry)提供高速缓存。这种“先大块、再细分”的模式是控制内部碎片的关键。

1.3 内存分配器的作用

内存分配器是内核内存管理的“最后一公里”执行者。不同的分配器服务于不同的场景和粒度需求。下表对比了 Linux 内核中主要的内存分配器及其职责:

分配器 管理粒度 主要用途 碎片类型
伙伴系统 (Buddy) 物理页框 (4KB ~ 4MB) 物理页框分配与回收;大块连续内存 外部碎片(页框无法合并)
Slab 分配器 小对象 (32B ~ 8KB) 内核对象缓存(inode、dentry、task_struct) 内部碎片(对象大小与 slab 不匹配)
页缓存 (Page Cache) 页 (4KB) 文件页与匿名页缓存;I/O 加速 页缓存碎片(回收延迟)
vmalloc 页 (4KB) 非连续 需要大块虚拟连续但物理不连续的内存 虚拟地址碎片
percpu 分配器 小对象 / 页 每 CPU 变量,避免锁竞争 每 CPU 碎片

下图展示了从伙伴系统到 Slab 分配器的完整分配链路,以及碎片产生的关键节点:

内存分配链路与碎片产生节点 伙伴系统 (Buddy Allocator) 管理 2^0 ~ 2^10 阶页块 分配: __alloc_pages_nodemask() 碎片风险: 外部碎片 / 阶块无法合并 Slab 分配器 管理 slab 缓存 (kmem_cache) 分配: kmem_cache_alloc() / kmalloc() 碎片风险: 内部碎片 / slab 内对象浪费 获取物理页框 碎片类型与影响 外部碎片 → 无法分配大块连续内存;内部碎片 → 内存利用率下降,缓存行效率降低 Slab 缓存实例 (常见内核对象) task_struct inode dentry buffer_head 每个缓存独立管理,对象大小固定,分配/释放 O(1)
碎片调优的核心矛盾: 伙伴系统追求大块连续物理内存,但频繁分配释放会导致外部碎片;Slab 分配器追求小对象快速复用,但对象大小与 slab 对齐不当会产生内部碎片。本课程后续章节将深入分析这两种碎片的成因、检测方法以及调优策略。

1.4 为什么 Slab 分配器是调优焦点

在 Linux 内核中,Slab 分配器承担着最频繁、最细粒度的内存分配任务。几乎所有内核数据结构的创建与销毁都经过 Slab 分配器。其性能与碎片状况直接影响系统整体吞吐量、响应延迟以及内存利用率。

  • 高频调用: 每次打开文件、创建进程、网络收发都会触发 Slab 分配。
  • 碎片敏感: 内部碎片虽然单次浪费小,但累积后可能达到数百 MB。
  • 调优可见: 通过 /proc/slabinfo 和 slabtop 可直接观察每个缓存的分配情况。
  • 与伙伴系统联动: Slab 回收页框时,若伙伴系统无法合并,则碎片问题向上传递。