2. slab分配器核心概念:对象缓存、slab、着色与硬件缓存对齐
slab分配器是Linux内核中用于管理小对象(通常小于一页)的高效内存分配机制。它通过预分配和缓存常用数据结构,显著减少了频繁分配/释放带来的碎片和性能开销。本章深入剖析其四大核心概念:对象缓存(Object Cache)、slab、着色(Color)与硬件缓存对齐。
2.1 对象缓存(Object Cache)
对象缓存是slab分配器对外暴露的抽象接口,每个缓存负责管理一种特定类型或大小的对象。内核中通过 kmem_cache_create() 创建缓存,通过 kmem_cache_alloc() / kmem_cache_free() 分配/释放对象。
task_struct、inode、dentry)预先分配好,避免每次使用时都从伙伴系统申请零散页面,从而消除外部碎片并加速分配路径。
2.1.1 缓存结构
每个 struct kmem_cache 包含以下核心字段:
| 字段 | 类型 | 说明 |
|---|---|---|
name |
const char * |
缓存名称,用于 /proc/slabinfo 标识 |
object_size |
unsigned int |
每个对象的大小(字节) |
align |
unsigned int |
对象对齐要求 |
flags |
slab_flags_t |
缓存标志(如 SLAB_HWCACHE_ALIGN) |
num |
unsigned int |
每个slab中可容纳的对象数量 |
gfporder |
unsigned int |
每个slab占用的页面阶数(2^gfporder 页) |
cpu_cache |
struct array_cache * |
每CPU对象缓存(快速路径) |
slabs_full |
struct list_head |
完全分配完的slab链表 |
slabs_partial |
struct list_head |
部分分配的slab链表 |
slabs_free |
struct list_head |
完全空闲的slab链表 |
2.1.2 缓存层次与分配路径
分配路径遵循三级缓存策略,从最快到最慢:
- 每CPU缓存(array_cache):无锁访问,直接返回已缓存的对象指针。
- slab链表:从
slabs_partial或slabs_free中取出对象。 - 伙伴系统:当所有slab都满时,从伙伴系统分配新页面创建新slab。
kmem_cache_alloc() 仅需几十条指令,远低于伙伴系统的页面分配开销。
2.2 slab:内存管理的物理单元
slab 是对象缓存下的实际内存载体,由一个或多个连续物理页面组成。每个slab被划分为多个等长的对象槽位,并包含管理数据。
2.2.1 slab布局
一个典型的slab内存布局如下:
+--------------------------------------------------+
| slab管理结构 (struct slab) | 着色区 (可选) |
+--------------------------------------------------+
| 对象 0 | 对象 1 | ... | 对象 N-1 |
+--------------------------------------------------+
| 空闲对象链表 (freelist) | 填充 (对齐用) |
+--------------------------------------------------+
- 管理结构:记录slab所属缓存、对象数量、空闲链表指针等。
- 着色区:用于硬件缓存对齐(见2.3节)。
- 对象数组:连续排列的固定大小对象。
- freelist:以数组或链表形式记录空闲对象索引。
2.2.2 slab状态与链表
每个缓存维护三个slab链表,对应slab的不同使用状态:
| 链表 | 状态 | 分配行为 |
|---|---|---|
slabs_full |
所有对象已分配 | 不可分配,等待释放 |
slabs_partial |
部分对象空闲 | 优先从此链表分配 |
slabs_free |
所有对象空闲 | 可回收或重新使用 |
slabs_free 链表中的slab数量超过阈值时,内核会触发slab回收,将空闲页面归还给伙伴系统,避免内存浪费。
2.3 着色(Color)与硬件缓存对齐
着色 是slab分配器为减少CPU硬件缓存(Cache)冲突而引入的优化技术。当多个slab中的对象映射到相同的缓存行时,会导致频繁的缓存失效,降低性能。
2.3.1 缓存冲突问题
假设每个slab大小为4KB,对象大小为64字节(与L1缓存行大小相同)。如果所有slab的起始地址都是4KB对齐,那么每个slab中第0个对象的地址在缓存中映射到同一组(set),导致严重的缓存颠簸(Cache Thrashing)。
2.3.2 着色原理
着色通过在slab起始处插入可变大小的偏移量(着色区),使不同slab中相同索引的对象映射到不同的缓存行。着色区的大小在 [0, color_max] 范围内循环变化。
2.3.3 着色参数计算
着色相关的关键参数由 calculate_slab_order() 和 cache_init_objs() 计算:
/* 伪代码:着色参数计算 */
color_max = cachep->gfporder * PAGE_SIZE; /* slab总大小 */
color_max -= cachep->num * cachep->object_size; /* 减去对象占用 */
color_max -= sizeof(struct slab); /* 减去管理结构 */
color_off = cachep->align; /* 对齐粒度 */
/* 每次创建新slab时,着色偏移循环递增 */
cachep->colour_next = (cachep->colour_next + 1) % cachep->colour_count;
offset = cachep->colour_next * color_off;
- colour_count:可用的着色偏移数量,等于
color_max / color_off。 - colour_off:每次偏移的步长,通常等于
L1_CACHE_BYTES(64字节)。 - colour_next:下一个slab使用的着色索引,循环递增。
2.4 硬件缓存对齐(Hardware Cache Alignment)
除了着色,slab分配器还通过 SLAB_HWCACHE_ALIGN 标志提供更直接的缓存对齐保证。当创建缓存时指定此标志,内核会确保:
- 每个对象的起始地址对齐到硬件缓存行大小(通常为64字节或128字节)。
- 对象之间不会跨缓存行,避免伪共享(False Sharing)问题。
2.4.1 对齐策略对比
| 策略 | 机制 | 内存开销 | 适用场景 |
|---|---|---|---|
| 着色(Color) | slab间偏移 | 低(每个slab少量填充) | 通用小对象缓存 |
| 硬件缓存对齐 | 对象起始地址对齐到缓存行 | 中(每个对象可能有填充) | 高频访问、多CPU共享的数据结构 |
| 两者结合 | 着色 + 对齐 | 较高 | 性能敏感型内核子系统(如网络、文件系统) |
2.5 核心概念关系总览
下图展示了对象缓存、slab、着色与硬件缓存对齐之间的整体架构关系:
2.6 关键数据结构与API
以下是与本章核心概念直接相关的关键数据结构和API:
/* 内核源码:include/linux/slab.h 和 mm/slab.c */
/* 创建对象缓存 */
struct kmem_cache *kmem_cache_create(const char *name,
unsigned int object_size,
unsigned int align,
slab_flags_t flags,
void (*ctor)(void *));
/* 从缓存分配对象 */
void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags);
/* 释放对象回缓存 */
void kmem_cache_free(struct kmem_cache *cachep, void *objp);
/* 销毁缓存 */
void kmem_cache_destroy(struct kmem_cache *cachep);
/* slab管理结构(简化版) */
struct slab {
struct list_head slab_list; /* 挂入 slabs_full/partial/free 链表 */
struct kmem_cache *cachep; /* 所属缓存 */
unsigned int inuse; /* 已分配对象数量 */
unsigned int free; /* 空闲对象索引(freelist头) */
void *s_mem; /* 对象数据区起始地址 */
unsigned int active; /* 活跃对象计数 */
};
/proc/slabinfo 可以查看系统中所有slab缓存的实时状态,包括对象总数、活跃数、每个slab的对象数等。使用 slabtop 命令可动态监控slab使用情况。
2.7 本章小结
- 对象缓存 是slab分配器的顶层抽象,每个缓存管理一种固定大小的对象,通过三级缓存路径加速分配。
- slab 是实际的内存载体,由连续物理页面组成,内部划分为对象槽位和freelist,通过三个链表管理状态。
- 着色 通过在slab起始处插入可变偏移量,使不同slab中相同索引的对象映射到不同缓存行,减少缓存冲突。
- 硬件缓存对齐 确保对象起始地址与缓存行边界对齐,避免多核环境下的伪共享问题。
- 着色与硬件缓存对齐可以组合使用,在性能敏感的内核子系统中达到最佳缓存利用率。