引言
Slub 分配器是 Linux 内核的默认通用内存分配器,作为 SLAB 分配器的现代化替代方案,自 2.6.23 内核(2007 年)起成为默认选择。它在简化设计的同时提供了卓越的性能,特别是对多核系统和 NUMA 架构的优化支持。本文将深入剖析 Slub 的核心架构、工作原理、调试接口以及生产环境调优实战。
一、分配器演进简史
Linux 内核先后经历了三代通用内存分配器:
- SLAB(1994):最初的 slab 分配器,设计精巧但代码复杂,管理开销随系统规模增长显著
- SLOB(2002):面向嵌入式系统的极简分配器,代码量极小但碎片化严重
- Slub(2007):在 SLAB 基础上重新设计,代码简洁、调试友好、per-CPU 优化彻底
Slub 的设计哲学是"用最简单的代码实现最佳性能"。它移除了 SLAB 中复杂的着色(coloring)和缓存空闲列表管理,通过 per-CPU 页和 per-CPU partial 列表即可获得极高的分配效率。
二、核心架构
2.1 三层管理结构
Slub 采用三级管理模型,这也是其与 SLAB 最根本的架构差异:
- Node 层:每个 NUMA 节点维护一个
kmem_cache_node,持有 full/partial slab 列表。在 UMA 系统中只有一个 node - CPU 层:每个 CPU 拥有独占的
kmem_cache_cpu,包含一个 active page 和一个 freelist 指针。快速路径分配完全无锁(无 preemption 情况下) - Page 层:每个 slab 由一个或多个物理页组成,页内对象连续排列
关键数据路径:当 CPU 需要分配对象时,首先检查 per-CPU freelist,若有空闲对象则直接弹出返回(快速路径);若为空则从 node partial 列表补充(中速路径);若 partial 也为空则从 buddy allocator 分配新页 slab(慢速路径)。
2.2 内存布局
Slub 的 slab 页内布局极为简洁。每个 slab 页通过 struct page 的特定字段(freelist、inuse、objects、frozen)进行元数据管理。对象本身在 slab 中线性排列,空闲对象通过链表串联。与 SLAB 将元数据放在外部数组不同,Slub 尽可能将元数据内联到 page 结构中,减少外部内存开销。
对于小对象(通常小于 1/8 页大小),Slub 还支持Red Zoning——在对象边界放置特殊魔数(0x1Slug),释放时检测以捕获溢出。此外, poisoning 机制释放对象时填充特定模式(如 0x6b 0xa5),帮助检测 use-after-free。
三、关键设计决策
3.1 Per-CPU Page 机制
Slub 的每个 CPU 可以"独占"一个 slab 页(frozen=1 时),该页完全从 node 层级抽离,仅服务于本 CPU。这种设计在多核场景下消除了绝大部分锁争用:快速路径中 CPU 直接操作自己的 page,无需原子操作或锁,性能接近无结构体的 Buddy 分配。
然而独占也带来碎片风险:若某 CPU 只用到 one object 便将 page frozen,剩余空间便浪费了。Slub 通过恢复机制平衡这一问题——当 CPU page 空闲对象过多时(超过阈值)归还给 node,或当 node partial 耗尽时回收 distant CPU pages。
3.2 NUMA 亲和性
每个 kmem_cache 的 node 列表天然实现 NUMA 感知分配。分配优先从本地 node partial 或 CPU page 取出对象;仅当本地耗尽时才 fallback 到远端 node,并通过 kmem_cache_alloc_node 允许调用者显式指定 node。部分调试信息中还能观察到 node 的 partial 分配统计。
3.3 GFP 标志处理
Slub 对分配标志的支持非常完整:GFP_DMA/GFP_DMA32 触发 zone fallback、GFP_ZERO 自动清零、GFP_RECLAIM 允许回内存回收、GFP_NOWARN 抑制警告等等。这些标志在底层影响 page 分配路径,在 CPU 快速路径中则无需关心。
四、Debug 与诊断工具
4.1 /proc/slabinfo
经典的 slab 统计接口,展示每个 kmem_cache 的活跃对象数、总对象数、对象大小、页数等。字段包括:active_objs、num_objs、objsize、objperslab、pagesperslab、active_slabs、num_slabs、tunables(如 limit、batchcount、shared factor)。
4.2 /sys/kernel/slab/<cache>/
Slub 提供了丰富的 sysfs 接口供运行时调优:
cpu_partial:per-CPU partial 列表的 slab 数量上限cpu_slab:指定某个 CPU 的 slab 缓存信息partial:node partial 列表中的 slab 总数objects与object_size:基本元信息order:slab 的 buddy allocator 阶数poison/red_zone/store_user:调试开关validate:强制完整性检查trace:启用分配追踪slab_size:重写对象对齐后大小min_partial:node partial 列表最小保留数
4.3 slabtop 与 debug 选项
slabtop 工具实时显示 slab 缓存使用排名。内核配置方面,CONFIG_SLUB_DEBUG 开启基础调试支持,CONFIG_SLUB_DEBUG_ON 启动时默认启用 poison/red_zone。
启动参数 slub_debug=UZP 可分别为所有缓存启用 User Tracking、Red Zone、Poisoning。这些虽然带来轻微开销,但在开发和测试环境中极有价值。
4.4 kmemleak 与 KASAN
Slub 与 CONFIG_DEBUG_KMEMLEAK 可配合检测内存泄漏。而 KASAN(Kernel Address Sanitizer)则需要在 Slub 中特别处理——编译器自动插桩检查,Slub 通过在对象布局中引入 redzone 区域(KASAN_SHADOW_OFFSET 映射的影子内存)记录访问越界。
五、生产环境调优实战
5.1 降低碎片化
多核系统中 Slub 的 per-CPU frozen page 容易造成"隐形碎片",即大量 slab 页因少量活跃对象被冻结在 CPU 上无法复用。监控 /proc/meminfo 中的 Slab 值与 SReclaimable 可以评估 slab 整体占用。若观察到某个缓存 num_slabs 远大于 active_slabs,说明碎片较高。
调优策略包括增大 cpu_partial(允许 partial 列表保留更多半满 slab)和 min_partial(维护 node partial 最小规模),以及在适当场景使用 SLAB_HWCACHE_ALIGN 提升缓存命中率。
5.2 大对象分配
对于超过一个大页(PAGE_SIZE << oo_max)的分配,Slub 直接 fallback 到 Buddy Allocator,不再走 slab 层级。根据对象的实际分布选择合适的 order 参数可减少大对象分配时的内部碎片。
5.3 NUMA 系统优化
在 NUMA 系统中注意 /sys/devices/system/node/node*/meminfo 观察本地/远端内存使用比例。kmem_cache_alloc_node 允许显式指定 node,高频分配的缓存应尽量选择本地节点。
5.4 性能诊断流程
当系统出现性能回落怀疑 slab 开销时,可按以下步骤排查:
slabtop -o查看 Top 占用 slab 缓存- 对照
/proc/slabinfo分析 active_objs/num_objs 比值 - 检查是否有异常的
kmem_cache_alloc调用频率(perf stat 或 ftrace) - 通过
/proc/vmallocinfo确认是否大量走 vmalloc 路径 - 若确认 slab 分配热点,考虑使用
__get_free_pages或 HugeTLB 绕过 slab 层
六、Slub 与 SLAB 性能对比
官方基准测试和实际内核测试表明:在多核(≥4 core)场景下,Slub 的分配/释放吞吐量比 SLAB 快 10%-30%,主要得益于 per-CPU page 消除的锁争用。在单核场景下两者性能相当。内存方面,Slub 由于移除了外部元数据阵列,整体内存开销比 SLAB 低 5%-15%。碎片方面 SLAB 因着色策略在小对象密集场景略优,但整体差异微小。
七、最新内核演进
近期内核开发中,Slub 持续引入改进:
- Tiny Rightsize 优化:针对 ≤ 64B 对象的专项路径,减少元数据开销
- Memory 分组 cgroup:Slub 已支持
memory_cgroup接口,实现按 cgroup 维度的 slab 内存计量 - random_kmalloc:安全加固特性,随机化 KMALLOC_* 缓存的底层实现以对抗堆喷攻击
- BPF Type Format (BTF):配合 BPF 实现运行时 slab 分配追踪和性能分析
- Folio 集成:随内核folio 抽象引入,未来可能重构 slab 与 compound page 的协作
八、总结
Slub 分配器以其简洁的设计和出色的多核扩展性成为 Linux 标准内存分配基础设施。在性能层面,CPU 快速路径几乎可以达到理论最优;在可观测性层面,/proc/slabinfo 和 /sys/kernel/slab 为运维提供了便捷的诊断入口。理解 Slub 的三层管理架构和 per-CPU page 机制,是深入 Linux 内核内存子系统的必经之路。
对于内核开发者,建议通过阅读 mm/slub.c 源码(当前约 5,000+ 行)配合本章描述的架构模型来系统理解;对于系统管理员,重点掌握 slabtop 和 sysfs 调优接口即可满足日常诊断需求。

发表评论 取消回复