4. Slab分配器核心概念:Slab、Cache、Object、着色(Coloring)原理

Slab分配器是Linux内核中用于管理小对象(通常小于一页)的高效内存分配机制。它通过预分配和缓存常用数据结构,显著减少了外部碎片并提高了分配/释放性能。本章深入剖析其四大核心概念:Cache、Slab、Object 和 着色(Coloring)。

4.1 核心概念总览

在深入细节之前,先通过下表建立对这四个概念的宏观认知:

概念 英文 角色 类比(仓库管理)
Cache kmem_cache 对象工厂与仓库 专门存放“螺丝钉”的货架区
Slab slab 物理内存容器 一个装满螺丝钉的纸板箱
Object object 被管理的数据单元 一颗独立的螺丝钉
着色 coloring 缓存行对齐优化 在纸箱内调整螺丝钉的起始位置
Slab分配器核心概念关系图 Cache (kmem_cache) Slab (满) Slab (部分) Slab (空) Object Object Object Object Object 一个Cache管理多个Slab,每个Slab包含多个连续排列的Object。 Slab根据内部Object的使用状态分为满、部分、空三种。

4.2 Cache:对象工厂

Cache 是Slab分配器的顶层管理结构,在内核中由 struct kmem_cache 表示。每个Cache负责管理一种特定大小或类型的对象。例如,内核为 task_struct(进程描述符)和 inode(索引节点)分别创建了独立的Cache。

4.2.1 Cache的关键属性

  • object_size:单个对象的大小(字节)。
  • align:对象对齐要求(如8字节、16字节对齐)。
  • flags:行为标志(如 SLAB_HWCACHE_ALIGN 表示硬件缓存行对齐)。
  • num:每个Slab中能容纳的对象数量。
  • gfporder:每个Slab占用的连续物理页框数量(以2的幂次表示)。

关键洞察:Cache通过 kmem_cache_create() 创建,通过 kmem_cache_alloc() 分配对象。这种设计将特定类型对象的分配请求集中管理,避免了通用分配器(如伙伴系统)处理小对象时产生的内部碎片。

4.3 Slab:物理内存容器

Slab 是Cache中实际承载物理内存的单元。一个Slab由一个或多个连续的物理页框组成(通常为1页,即4KB)。Slab内部被划分为多个等长的Object。

4.3.1 Slab的三种状态

每个Cache维护三个Slab链表,用于快速定位可用内存:

链表名称 状态 分配策略
slabs_full 所有Object均已分配 不可用于分配,等待释放
slabs_partial 部分Object空闲 优先从此链表分配,减少碎片
slabs_free 所有Object均空闲 可被回收或重新填充

性能要点:分配器总是优先从 slabs_partial 链表中分配对象。只有当所有部分Slab都满了,才会从 slabs_free 中获取一个空Slab进行填充。这种策略最大限度地利用了已有内存,减少了向伙伴系统申请新页框的开销。

4.4 Object:被管理的数据单元

Object 是Slab分配器管理的最小单位,是内核实际使用的数据结构实例。每个Object的大小在Cache创建时固定。

4.4.1 Object的布局

在一个Slab内部,Object是连续排列的。每个Object之间可能存在填充字节以满足对齐要求。Slab的起始部分通常包含一个 描述符(descriptor),用于管理该Slab的元数据。

/* Slab内部布局示意 */
+-------------------+-------------------+-------------------+-----+
| Slab Descriptor   | Object 0          | Object 1          | ... |
| (管理元数据)       | (数据结构实例)     | (数据结构实例)     |     |
+-------------------+-------------------+-------------------+-----+
| <--- 对齐填充 ---> | <--- object_size ---> | <--- object_size ---> |

4.4.2 空闲对象链表

Slab分配器使用一个空闲对象链表(free list)来追踪哪些Object是可用的。这个链表通常存储在Slab描述符中,或者直接嵌入在空闲Object本身内部(当Object足够大时)。

Slab内部Object与空闲链表 Slab (物理页框) Obj 0 Obj 1 Obj 2 Obj 3 Obj 4 Obj 5 空闲链表头 next next NULL 空闲对象 已分配对象

4.5 着色(Coloring)原理

着色(Coloring) 是Slab分配器中最精妙的设计之一,其核心目标是优化CPU缓存(Cache)利用率,减少缓存行颠簸(Cache Line Thrashing)。

4.5.1 问题背景

当多个Slab中的Object在物理内存中具有相同的偏移量时,它们会映射到CPU缓存的同一组(Cache Set)中。如果这些Object被频繁交替访问,会导致缓存行频繁换入换出,性能急剧下降。

4.5.2 着色如何工作

着色通过在Slab的起始位置引入一个可变的偏移量(称为颜色),使得不同Slab中Object的起始地址在缓存行级别上错开。这个偏移量是缓存行大小(通常为64字节)的整数倍。

/* 着色偏移示意 */
Slab 0: [偏移0] [Object 0] [Object 1] ...
Slab 1: [偏移64] [Object 0] [Object 1] ...  (颜色=1)
Slab 2: [偏移128] [Object 0] [Object 1] ... (颜色=2)
...
着色(Coloring)原理:缓存行错开 CPU Cache Set (缓存组) 无着色:所有Slab的Object 0映射到同一缓存行 → 缓存行颠簸,性能下降 有着色:Slab 0 (偏移0) | Slab 1 (偏移64) | Slab 2 (偏移128) → 映射到不同缓存行,减少冲突 Slab 0: 偏移 0 Slab 1: 偏移 64 Slab 2: 偏移 128 颜色值 = 偏移量 / 缓存行大小。颜色范围通常为 [0, color_max]。

4.5.3 着色与内存开销

着色虽然带来了性能提升,但也会引入一定的内存开销,因为每个Slab的可用Object数量可能会因为偏移而减少。内核在 color_max 的计算中平衡了性能与内存效率。

注意事项:着色优化仅在 SLAB_HWCACHE_ALIGN 标志被设置时生效。对于非性能关键的小对象,内核可能选择禁用着色以节省内存。着色偏移量存储在Slab描述符的 colouroff 字段中。

4.6 核心数据结构关系

以下代码展示了Slab分配器核心数据结构之间的引用关系:

/* 简化的内核数据结构关系 */
struct kmem_cache {          // Cache
    const char *name;        // 名称,如 "task_struct"
    unsigned int object_size; // 对象大小
    unsigned int align;      // 对齐要求
    unsigned int num;        // 每个Slab的对象数
    unsigned int colour;     // 着色偏移
    struct list_head slabs_full;    // 满Slab链表
    struct list_head slabs_partial; // 部分Slab链表
    struct list_head slabs_free;    // 空Slab链表
};

struct slab {                // Slab
    struct list_head list;   // 链表节点
    unsigned long colouroff; // 着色偏移量
    void *s_mem;             // 第一个Object的起始地址
    unsigned int inuse;      // 已分配对象数
    unsigned int free;       // 空闲对象数
    struct kmem_cache *cache; // 所属Cache
};

4.7 本章小结

  • Cache 是对象类型的工厂,每种数据结构拥有独立的Cache。
  • Slab 是物理内存容器,通过三个链表(满、部分、空)管理生命周期。
  • Object 是分配的最小单元,通过空闲链表高效追踪。
  • 着色 通过引入可变偏移量,将不同Slab的Object映射到不同的CPU缓存行,有效减少缓存行颠簸,是Slab分配器性能优化的关键手段。

实践要点:在分析内存碎片时,检查 /proc/slabinfo 可以查看每个Cache的Slab使用情况。如果某个Cache的 slabs_partial 数量远大于 slabs_full 和 slabs_free,说明该类型对象的内存使用较为分散,可能存在内部碎片问题。