☰
slub alloc_tagging_slab_alloc_hook
2026/10/31 2:15:08 网站建设 项目流程

alloc_tagging_slab_alloc_hook()是内存分配追踪(alloc_tag)机制在 SLUB 分配路径上的外层钩子,负责用静态键快速判断“要不要追踪”,再决定是否调用真正的记录函数__alloc_tagging_slab_alloc_hook()。

一、函数签名

static inline void alloc_tagging_slab_alloc_hook(struct kmem_cache *s, void *object, gfp_t flags)
参数含义
s缓存
object刚分配的对象
flags分配标志

无返回值。

二、典型实现

static inline void alloc_tagging_slab_alloc_hook(struct kmem_cache *s, void *object, gfp_t flags) { #ifdef CONFIG_MEM_ALLOC_PROFILING if (mem_alloc_profiling_enabled()) __alloc_tagging_slab_alloc_hook(s, object, flags); #endif }

逻辑:

  1. 条件编译:只有CONFIG_MEM_ALLOC_PROFILING开启时才有实际代码。

  2. 静态键检查:mem_alloc_profiling_enabled()读静态键,判断追踪是否运行时开启。

  3. 调用内层:开启时调__alloc_tagging_slab_alloc_hook()做实际记录。

三、为什么分成内外两层

这是内核里“静态键检查 + noinline 实际工作”的经典模式:

层次函数属性职责
外层alloc_tagging_slab_alloc_hookstatic inline静态键检查,极轻量
内层__alloc_tagging_slab_alloc_hookstatic noinline实际记录,代码较大

外层必须内联

static inline void alloc_tagging_slab_alloc_hook(...) { if (mem_alloc_profiling_enabled()) __alloc_tagging_slab_alloc_hook(...); }
  • 调用点在slab_post_alloc_hook()的循环里,每次分配都调用。

  • 内联后,编译器把静态键判断编成一条nop或jmp(jump label 机制)。

  • 追踪关闭时(默认),这条判断几乎零开销,且不会产生函数调用。

  • 若外层不内联,每次分配都要压栈调用,即使追踪关闭也付调用开销。

内层必须 noinline

static noinline void __alloc_tagging_slab_alloc_hook(...) { // 实际记录逻辑,代码较大 }
  • 实际记录涉及读写slabobj_ext、codetag_ref等,代码体积较大。

  • 如果内联进slab_post_alloc_hook(),会让热路径代码膨胀,影响 icache。

  • noinline保证它作为独立函数存在,只在追踪开启时才被调用,平时不占热路径空间。

四、静态键mem_alloc_profiling_enabled()

if (mem_alloc_profiling_enabled())
  • mem_alloc_profiling_enabled()读一个static_key(类似前面的slub_debug_enabled、strict_numa)。

  • 追踪默认关闭,静态键为 false,判断被编译成nop。

  • 运行时通过 sysfs 或启动参数开启后,内核用 jump label打补丁成jmp,跳转到内层函数。

  • 关闭时零开销,开启时才有实际成本。

这是“编译期裁剪 + 运行时零开销开关”的又一例,与USE_LOCKLESS_FAST_PATH()、slub_debug_enabled一脉相承。

五、在分配流程中的位置

slab_post_alloc_hook(s, objcg, flags, size, p) │ ├─ memcg_slab_post_alloc_hook(...) // memcg 计费 │ ├─ for (i = 0; i < size; i++) │ ├─ prepare_slab_obj_exts_hook(s, flags, p[i]) // 准备扩展向量 │ └─ alloc_tagging_slab_alloc_hook(s, p[i], flags) // ← 本函数 │ └─ __alloc_tagging_slab_alloc_hook(...) // 记录分配点 │ └─ ...
  • prepare_slab_obj_exts_hook()先确保slabobj_ext向量存在。

  • alloc_tagging_slab_alloc_hook()再把对象的codetag_ref指向当前分配点。

两者配合:先有存储(扩展向量),再写内容(分配标签)。

六、与__alloc_tagging_slab_alloc_hook()的分工

维度alloc_tagging_slab_alloc_hook__alloc_tagging_slab_alloc_hook
属性static inlinestatic noinline
职责静态键检查实际记录
开销追踪关闭时零开销只在开启时执行
代码体积极小,可内联较大,独立函数
调用频率每次分配仅追踪开启时

七、与前面知识的呼应

前面讲过这里体现
prepare_slab_obj_exts_hook()准备扩展向量
slabobj_ext/codetag_ref记录分配标签的存储
mem_alloc_profiling_enabled()静态键,控制追踪开关
static_key/ jump label关闭时零开销
slub_debug_enabled/strict_numa同类静态键机制
slab_post_alloc_hook()调用点
__alloc_tagging_slab_alloc_hook()内层实际工作函数

八、设计要点总结

  1. 条件编译:CONFIG_MEM_ALLOC_PROFILING未开时函数为空。

  2. 静态键门控:mem_alloc_profiling_enabled()让关闭时零开销。

  3. 内外分层:外层 inline 做轻量检查,内层 noinline 做实际记录。

  4. 热路径友好:默认关闭时几乎无成本,开启时才付代价。

  5. 与扩展向量配合:依赖prepare_slab_obj_exts_hook()先准备好存储。

九、总结

alloc_tagging_slab_alloc_hook(s, object, flags)是 SLUB 分配路径上 alloc_tag 追踪的外层钩子:用static inline+ 静态键mem_alloc_profiling_enabled()做零开销判断,追踪开启时才调用noinline的__alloc_tagging_slab_alloc_hook()记录对象的分配点。它体现了内核“编译期裁剪 + 运行时零开销开关 + 内外分层控制代码体积”的典型设计。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询