人人都会AI编程

6.3 内存分配机制:伙伴系统、Slab 分配器

更新时间:2026-07-12

Linux 内核在管理物理内存时,面对的是两种截然不同的需求:一是分配大块、物理地址连续的内存页(比如为某个驱动程序提供 DMA 缓冲区);二是频繁地分配和释放大量小对象(比如进程描述符、文件节点、网络缓冲区)。如果只用一种通用内存分配器,要么因碎片严重浪费内存,要么因锁争用损失性能。为此,内核采用了两层核心机制:伙伴系统负责以页为单位的粗粒度分配,而 Slab 分配器则在伙伴系统提供的页上构建对象缓存,实现细粒度、高效率的分配。理解这两者,是查看内存使用、排查内存泄漏、优化系统性能的基础。


1. 伙伴系统:物理页框的管理者

伙伴系统的核心任务是把物理内存划分为大小为 2^order 个连续页的块(一页通常是 4 KB),并尽量保证外部碎片可控。当你请求 8 页连续内存时,伙伴系统会从空闲块中找到一个大小刚好等于或稍大的块,必要时将大块分裂成两个“伙伴”,直到得到合适大小的块;释放内存时则会检查相邻伙伴是否空闲,尝试合并成大块还给更高阶,以减少碎片。

实际查看与监控
通过 /proc/buddyinfo 可以实时观察各内存区域(Node 和 Zone)中各阶空闲块的数量:

cat /proc/buddyinfo

示例输出(简化):

Node 0, zone   Normal   23   18   7   4   2   1   0   0   0   0   0

每一列代表从 order 0(单页,4 KB)到 order 10(2^10 页=4 MB)的空闲块数量。如果高阶(右侧)长期为零,意味着连续大页缺失,可能会导致需要高阶分配的操作(如某些 DMA、大页需求)失败。

实用提示

  • 如果 buddyinfo 显示高阶内存始终不足,可以尝试提前通过内核参数预留大页(HugePages),或者检查是否存在内存碎片化问题,必要时启用内存紧缩(compaction)。
  • 内存分配时通过 GFP_* 标志控制行为:GFP_KERNEL(常用,可能睡眠)、GFP_ATOMIC(不能睡眠,用于中断上下文)等。在查看内核日志内存分配失败(page allocation failure)时,留意失败时的 order 和 gfp_mask,有助于定位问题。

2. Slab 分配器:内核对象的“专用缓存”

伙伴系统以页为单位,但内核中充斥着大量几字节到几 KB 大小的结构体。若直接调用伙伴系统,每个小对象都占用完整一页会导致惊人的内存浪费(内部碎片),且初始化/销毁频繁也会拖慢速度。Slab(以及它的改进版 Slub)在伙伴系统分配的页面上构建对象缓存,预先把内存切成大小相同的槽位,并保留已销毁对象的“惰性”状态以便快速复用,从而大幅降低碎片和分配开销。

常用观察手段
/proc/slabinfo 列出了内核中所有 slab 缓存的统计:

cat /proc/slabinfo
# 或更人性化的查看工具
slabtop

运行 slabtop 会动态显示各个 slab 的名称、对象总数、活跃数、占用内存等。典型输出片段:

  OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME
229206 175953  76%    1.12K   8182      28     32728K dentry
 55683  55683 100%    0.57K   2064      27     16512K radix_tree_node
  • dentry(目录项缓存)这类高频缓存值得关注:活跃对象比例过低(如 10%)可能意味着某些目录项用过即弃,缓存利用率不高。
  • SIZE 列显示每个对象大小,CACHE SIZE 是该 slab 总共占用的内存。若某类 slab 占用异常高,可用 echo 2 > /proc/sys/vm/drop_caches 尝试回收可释放的缓存(注意这属于临时的调试手段,不是常规操作)。

与 slab 相关的内核内存泄漏排查
如果一个 slab 的 OBJS 持续增长而不缩减,且 ACTIVE 几乎等于 OBJS,可能表明内核中有对象泄漏。配合 kmemleak(内核内存泄漏检测工具)或 /sys/kernel/debug/slab/<cache_name> 可以进一步分析每个缓存的具体情况。对于开发者,编写内核模块时务必注意配对使用分配与释放函数(如 kmem_cache_alloc / kmem_cache_free)。


3. 实际场景中的关联

  • 建站运维:发现 dentryinode_cache slab 占用了大量内存,且系统没有真正的内存压力,这通常只是 Linux 把空闲内存用作文件系统缓存,属于良性。但如果 slabtop 显示某些 slab 的 USE 极低而 CACHE SIZE 很高,说明有内存搁在那里未被回收,可以进一步用 free -mvmstat 观察 buffer/cache 的变化。
  • 容器/Pod 内存限制:容器内看到的 Slab 内存(/proc/meminfo 中的 Slab: 字段)其实是宿主机内核的全局 slab 使用量,因为容器共享内核。设定 Pod 资源限制时,无法单独限制 slab 用量,只能通过 node 级别的监控观察异常驱动或模块导致的 slab 飙升。
  • 性能调优:某些 slab 可通过内核参数修改其行为,例如网络密集型场景下可调整 net.core.somaxconn 等,间接影响相关缓存大小,但这类调优需结合具体应用压测。

小结
伙伴系统为 slab 分配器提供大块内存页,slab 再高效地“零售”给内核各种对象,两者共同构成了 Linux 内核内存分配的骨架。作为运维或开发人员,通过 buddyinfoslabtop 定期检查,可以快速定位内存碎片、泄漏以及不合理缓存占用问题,在不碰内核代码的前提下掌握系统内存健康状态。