1. 内存管理概述:物理内存与虚拟内存、伙伴系统的局限性、引入slab分配器的动机

1.1 物理内存与虚拟内存:内核视角的地址空间

在Linux系统中,内存管理单元(MMU)将CPU发出的虚拟地址转换为物理地址。内核必须同时管理这两种地址空间:物理内存是有限的硬件资源,而虚拟内存为每个进程提供独立的、连续的地址空间。

核心概念: 物理内存由页框(Page Frame)组成,通常大小为4KB。虚拟内存通过页表映射到物理页框,实现了隔离、按需分页和交换功能。

内核直接管理物理内存,其虚拟地址空间与物理内存之间存在线性映射(如直接映射区,即低端内存)。对于高端内存(超过896MB的物理内存),内核需要通过临时映射来访问。

物理内存与虚拟内存映射关系 物理内存与虚拟内存映射关系 虚拟地址空间 进程A 页表 VMA区域 缺页异常 MMU 页表转换 物理地址空间 页框0 页框1 页框N ZONE_DMA/ZONE_NORMAL 内核映射 直接映射区 vmalloc区 固定映射区 高端内存 线性偏移 PAGE_OFFSET 内核通过页表将虚拟地址映射到物理页框,物理内存被划分为不同的管理区(Zone)。

1.2 伙伴系统(Buddy System):物理页框分配器

Linux内核使用伙伴系统作为物理页框分配的核心算法。它将空闲页框按2的幂次方(order)组织成链表,用于分配和释放连续的物理内存块。

阶数 (Order) 连续页框数 大小 (4KB页) 典型用途
0 1 4 KB 单个页框分配
1 2 8 KB 小DMA缓冲区
2 4 16 KB 文件系统元数据
3 8 32 KB 网络协议栈
4 16 64 KB 中等大小内核对象
10 1024 4 MB 大块连续内存
工作原理: 当请求order为n的块时,若对应链表非空则直接分配;否则从更高阶链表分裂,直到满足需求。释放时检查伙伴块是否空闲,若空闲则合并回更高阶。
伙伴系统分配与合并流程 伙伴系统分配与合并流程 请求 order=2 order=2 链表空? 从 order=3 分裂 分配 释放 & 合并伙伴 回收到 order=3 分配时向上分裂,释放时向下合并。伙伴块必须满足地址对齐和大小相同两个条件。

1.3 伙伴系统的局限性

尽管伙伴系统在管理物理页框方面非常高效,但它并不适合内核中频繁发生的小对象分配与释放场景。其局限性主要体现在以下几个方面:

  • 内部碎片严重: 分配的最小单位是单个页框(4KB)。如果内核需要分配一个仅几十字节的对象(如 task_struct、inode),伙伴系统会浪费大量内存。
  • 分配速度慢: 对于小对象,每次分配都需要经过伙伴系统的分裂/合并操作,时间开销大,无法满足高频分配场景。
  • 缺乏对象缓存: 伙伴系统不感知对象类型,每次分配/释放都需要重新初始化对象,无法复用已构造的对象状态。
  • CPU缓存不友好: 频繁分配和释放相同大小的对象时,对象可能分散在不同的页框中,导致CPU缓存命中率下降。
问题 具体表现 影响
内部碎片 分配32字节对象占用4KB页框 内存利用率极低(<1%)
分配延迟 分裂/合并操作耗时 影响实时性和吞吐量
无对象缓存 每次分配后需重新初始化 增加CPU开销
缓存局部性差 对象分散在不同页框 降低L1/L2缓存命中率
性能瓶颈: 在极端情况下,内核频繁分配和释放小对象(如网络连接、文件描述符)时,伙伴系统的开销可能占CPU时间的10%以上,成为系统性能的瓶颈。

1.4 引入slab分配器的动机

为了解决伙伴系统的上述局限性,Sun Microsystems在Solaris 2.4中首次引入了slab分配器,随后被Linux内核采用。引入slab分配器的核心动机包括:

  1. 消除内部碎片: slab分配器将页框划分为多个相同大小的对象槽,精确匹配内核对象的大小,将碎片降至最低。
  2. 提高分配/释放速度: 通过维护每个CPU的本地缓存(per-CPU cache)和部分空闲链表,slab分配器可以在常数时间内完成分配和释放,避免伙伴系统的分裂合并开销。
  3. 对象缓存与复用: slab分配器支持对象构造函数和析构函数,分配时直接返回已构造好的对象,释放时仅标记为空闲,大幅减少初始化开销。
  4. 硬件缓存友好: slab分配器尽量将同一类型的对象集中存放在连续的页框中,提高CPU缓存的局部性。
  5. 统计与调试支持: slab分配器提供丰富的统计信息(如对象使用率、缓存命中率),便于内核开发者进行性能分析和内存泄漏检测。
伙伴系统 vs slab分配器对比 伙伴系统 vs slab分配器对比 伙伴系统 分配单位:页框 (4KB) 内部碎片:严重 分配速度:O(log N) 对象缓存:无 缓存局部性:差 优化 slab分配器 分配单位:对象 (字节级) 内部碎片:几乎为零 分配速度:O(1) 对象缓存:有 (构造/析构) 缓存局部性:优秀 slab分配器建立在伙伴系统之上,从伙伴系统获取页框,然后细分为对象进行管理。
关键设计思想: slab分配器并不替代伙伴系统,而是作为伙伴系统之上的对象缓存层。它从伙伴系统获取整页框,然后将其划分为多个相同大小的对象槽,专门服务于内核小对象的频繁分配与释放需求。