4. slab分配器核心数据结构:kmem_cache、slab、object的三级结构详解
slab分配器通过三级数据结构实现高效的内存管理:kmem_cache(缓存描述符)、slab( slabs 描述符)和 object(对象)。这种分层设计将内存池化、对象复用与伙伴系统的物理页管理解耦,是 Linux 内核内存子系统性能的关键。
核心思想:kmem_cache 管理同类型对象;每个 kmem_cache 包含多个 slab;每个 slab 由连续物理页组成,内部划分为等长 object。分配时优先从部分满的 slab 获取对象,避免碎片与频繁的伙伴系统交互。
4.1 三级结构总览
下图展示了 kmem_cache → slab → object 的层次关系与核心字段:
4.2 kmem_cache:缓存描述符
每个 kmem_cache 实例管理一种特定类型或大小的对象。内核通过 kmem_cache_create() 创建,通过 kmem_cache_destroy() 销毁。其核心字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
name |
const char * | 缓存名称,用于 /proc/slabinfo 标识 |
object_size |
unsigned int | 每个对象的大小(字节),已对齐 |
gfporder |
unsigned int | 每个 slab 占用的连续页阶数(2^gfporder 页) |
flags |
unsigned long | 缓存标志,如 SLAB_HWCACHE_ALIGN、SLAB_PANIC |
num |
unsigned int | 每个 slab 可容纳的对象数量 |
slabs_full |
struct list_head | 所有对象已分配的 slab 链表 |
slabs_partial |
struct list_head | 部分对象已分配的 slab 链表(分配首选) |
slabs_free |
struct list_head | 所有对象空闲的 slab 链表 |
ctor |
void (*)(void *) | 对象构造函数(可选),分配时初始化 |
性能提示:kmem_cache 的
gfporder 和 num 在创建时由 slab 分配器自动计算,目标是使 slab 大小不超过一个页面(小对象)或合理利用大页。可通过 /proc/slabinfo 查看每个缓存的 object_size、num 和 active_objs。
4.3 slab:物理页与对象的管理单元
slab 是连接伙伴系统物理页与上层对象的桥梁。每个 slab 对应 2^gfporder 个连续物理页,其描述符 struct slab 可以存放在 slab 内部(小对象)或外部(大对象)。
4.4 object:最小分配单元
object 是 slab 分配器返回给调用者的内存块。每个 object 的大小由 kmem_cache 的 object_size 决定,且经过对齐(硬件缓存行对齐可提升性能)。
空闲 object 的管理方式有两种:
- 内置空闲链表(传统 slab):在空闲 object 的头部存储指向下一个空闲 object 的指针,形成单向链表。分配时从链表头取,释放时插回链表头。
- kmem_bufctl_t 数组(部分架构):在 slab 描述符后附加一个索引数组,每个空闲 object 对应一个索引,指向下一个空闲对象。
注意:object 在分配时不会清零(除非指定 __GFP_ZERO)。释放后,object 的内容可能残留敏感数据,内核安全模块(如 slab_freelist_random)会随机化空闲链表顺序以降低堆喷攻击风险。
4.5 三级结构协同工作流程
以下步骤展示一次 kmem_cache_alloc() 调用如何穿越三级结构:
- 定位 kmem_cache:根据对象类型或大小找到对应的
kmem_cache实例。 - 选择 slab:优先从
slabs_partial链表头取一个 slab;若为空,则从slabs_free取;若都为空,则调用伙伴系统分配新页创建 slab。 - 获取 object:从 slab 的空闲链表头取出一个 object,更新
free指针和inuse计数。 - 状态迁移:若 slab 的
inuse达到num(全满),将其从slabs_partial移到slabs_full;若从空变为部分满,则移入slabs_partial。 - 返回 object:返回 object 的虚拟地址给调用者。
4.6 三级结构的性能优势
| 特性 | 说明 | 性能收益 |
|---|---|---|
| 对象复用 | 释放的 object 直接回收到 slab 空闲链表,不归还伙伴系统 | 避免频繁的页分配/释放,降低延迟 |
| 部分满优先 | 分配优先从 slabs_partial 取,避免全满 slab 的碎片 | 提高内存利用率,减少外部碎片 |
| 着色 (coloring) | 不同 slab 的 object 起始地址偏移不同缓存行 | 减少 CPU 缓存行冲突,提升多核访问性能 |
| NUMA 感知 | 每个 NUMA 节点有独立的 slab 链表 | 分配本地内存,避免跨节点访问 |
| 批量操作 | kmem_cache_alloc_bulk / free_bulk 一次操作多个 object | 减少链表遍历和锁竞争 |
总结:kmem_cache → slab → object 三级结构是 slab 分配器的核心设计。kmem_cache 提供类型隔离与统计;slab 管理物理页与对象状态;object 实现快速分配与释放。理解这一层级关系是优化内核内存分配性能、排查 slab 相关问题的前提。