☰
slub slab_fix
2026/10/28 1:59:09 网站建设 项目流程

slab_fix()是 SLUB 调试里报告“已尝试修复”的函数,与slab_bug()类似但语义不同:它说明系统发现异常并自动做了修正,而不是单纯报错。

一、函数签名

__printf(2, 3) static void slab_fix(struct kmem_cache *s, const char *fmt, ...)
项含义
__printf(2, 3)告诉编译器:第 2 个参数是格式串,第 3 个起是可变参数,便于编译期检查格式匹配
s出错的缓存
fmtprintf 风格格式串
...可变参数

二、__printf(2, 3)属性

__printf(2, 3)
  • 这是 GCC/Clang 的format属性封装,内核定义为__attribute__((format(printf, 2, 3)))。

  • 含义:函数第 2 个参数是 printf 格式串,第 3 个参数是可变参数的起始。

  • 作用:编译期检查fmt与后续参数类型是否匹配,比如:

slab_fix(s, "Fixed %d bytes", ptr); // 编译警告:%d 期望 int,给了指针
  • 对slab_bug()等同类函数也常加这个属性,只是你贴的这段里显式出现了。

三、struct va_format机制

struct va_format vaf; va_list args; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; pr_err("FIX %s: %pV\n", s->name, &vaf); va_end(args);

为什么用va_format而不是直接vprintk

C 语言里,...展开成va_list后,不能直接传给pr_err(pr_err是可变参数函数,不是v版本)。要输出va_list,通常有两条路:

  1. 用vprintk(fmt, args)之类的v版本。

  2. 把va_list打包进struct va_format,再用%pV格式符在普通pr_err里输出。

slab_fix()选了第 2 种,因为想在格式串里额外加前缀("FIX %s: "),把缓存名和错误信息拼在一行:

struct va_format { const char *fmt; va_list *va; };
字段含义
fmt原始格式串
va指向va_list的指针

%pV是内核 printk 的扩展格式符,专门用于“打印一个struct va_format”,内部会展开vaf->fmt和vaf->va。

输出效果

slab_fix(s, "Fixed %d bytes", n);

输出:

FIX kmalloc-64: Fixed 8 bytes

pr_err("FIX %s: %pV\n", s->name, &vaf)中:

  • %s→s->name(缓存名)。

  • %pV→ 展开vaf,即fmt+ 参数。

  • 前缀"FIX "表明这是“修复”而非单纯报错。

四、KUnit 集成

if (slab_add_kunit_errors()) return;

与slab_bug()一样,KUnit 测试环境下静默计数、不打印,避免刷屏,让测试断言错误次数。

五、与slab_bug()的对比

维度slab_bug()slab_fix()
语义报告错误报告已修复
输出前缀BUG(在__slab_bug内)FIX
底层转交__slab_bug()自己用pr_err+%pV
KUnit检查slab_add_kunit_errors()同
格式检查通常也有__printf显式__printf(2, 3)
可变参数处理va_start→__slab_bugva_start→va_format→pr_err

为什么slab_fix不转交__slab_bug

__slab_bug()的输出格式可能带分隔线、BUG前缀、dump_stack()等,适合“严重错误”。而“已修复”通常不是致命错误,只需一行日志说明修了什么,不需要调用栈和分隔线。所以slab_fix()自己用pr_err输出,更轻量。

六、使用场景

slab_fix()在 SLUB 调试里用于发现异常但能自动纠正的情况,典型如:

  • freelist 损坏但可修复:把损坏的 freepointer 重置。

  • inuse 计数不一致:重新计算并修正。

  • 红区被轻微破坏:记录并恢复。

  • 对象未正确初始化:补上初始化。

典型调用:

// 例如 freelist 上发现一个无效对象,跳过并修正 if (!check_valid_pointer(s, slab, p)) { slab_fix(s, "Freepointer corrupt, skipping"); // 修正 freelist }

输出:

FIX kmalloc-64: Freepointer corrupt, skipping

与slab_bug()(报告致命错误)配合,形成“严重错误报 BUG,可修复问题报 FIX”的分工。

七、%pV与struct va_format的价值

  • 统一可变参数输出:把va_list包装后可在任意pr_*里用%pV输出。

  • 支持前缀拼接:"FIX %s: %pV"把固定文本、缓存名、动态消息拼一行。

  • 内核标准做法:printk家族的%pV专门为此设计,广泛用于dev_err、netdev_err等。

对比直接vprintk:

方式优点缺点
vprintk(fmt, args)直接不能方便加前缀、拼接
struct va_format+%pV可拼接、可加前缀多一个结构体

slab_fix()需要加"FIX %s: "前缀,所以用%pV更自然

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

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

立即咨询