定义分配/释放跟踪的枚举,以及sysfs / debugfs 接口的编译期桩函数。
一、跟踪项枚举
enum track_item { TRACK_ALLOC, TRACK_FREE };配合上一段的struct track使用。SLUB 在开启SLAB_STORE_USER调试标志后,会在每个对象关联一个struct track数组,记录该对象最近的操作历史。
track_item标明这次记录的是哪种操作:
| 值 | 含义 |
|---|---|
TRACK_ALLOC | 记录“分配”事件,写入是谁、在哪、何时分配了这个对象 |
TRACK_FREE | 记录“释放”事件,写入是谁、在哪、何时释放了这个对象 |
典型用法
// 分配时 set_track(s, object, TRACK_ALLOC); // 释放时 set_track(s, object, TRACK_FREE);
set_track()内部会填充struct track的addr、handle、cpu、pid、when字段,并调用save_stack()(或save_stack_depot())保存调用栈。
为什么是数组
TRACK_ADDRS_COUNT 16说明这不是单个记录,而是每个对象一小段历史。常见设计是:
struct track { ... } track[TRACK_ADDRS_COUNT]; // 或按 track_item 索引更准确地说,SLUB 通常为每个对象维护最近 16 次操作的环形记录(在CONFIG_SLUB_DEBUG且SLAB_STORE_USER下,通过struct track数组按TRACK_ADDRS_COUNT组织),用来追溯:
对象被谁分配、谁释放
是否重复释放
是否释放后又被使用(结合 poison)
内存泄漏时最后一次分配者是谁
TRACK_ALLOC/TRACK_FREE就是区分这些历史记录的标签。
二、sysfs 接口的条件编译
#ifdef SLAB_SUPPORTS_SYSFS static int sysfs_slab_add(struct kmem_cache *); static int sysfs_slab_alias(struct kmem_cache *, const char *); #else static inline int sysfs_slab_add(struct kmem_cache *s) { return 0; } static inline int sysfs_slab_alias(struct kmem_cache *s, const char *p) { return 0; } #endif背景:/sys/kernel/slab/
SLUB 会为每个 kmem_cache在 sysfs 下创建条目,路径通常是:
/sys/kernel/slab/<cache_name>/
里面暴露属性如object_size、objs_per_slab、order、slabs、objects、cpu_partial、partial、aliases、shrink、trace等,用于查看和调优每个缓存。
sysfs_slab_add()负责注册一个缓存对应的 sysfs 目录和属性。
sysfs_slab_alias()负责创建别名:当两个缓存因为合并(CONFIG_SLAB_MERGE_DEFAULT)而实际共用同一个底层缓存时,sysfs 下通过别名指向同一个目录,避免重复创建。
SLAB_SUPPORTS_SYSFS
这个宏由体系结构或配置决定是否支持 slab sysfs 接口。支持时声明真正的函数原型(定义在mm/slub.c的后面或mm/slab_common.c),不支持时提供空的内联桩:
static inline int sysfs_slab_add(struct kmem_cache *s) { return 0; } static inline int sysfs_slab_alias(struct kmem_cache *s, const char *p) { return 0; }桩函数返回 0,调用点(如kmem_cache_create里)无需#ifdef,代码更干净,且不支持的平台上调用完全被优化掉。
这是内核里很常见的“接口 + 空桩”模式:把条件编译集中在头文件,业务代码统一调用。
三、debugfs 接口的条件编译
#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_SLUB_DEBUG) static void debugfs_slab_add(struct kmem_cache *); #else static inline void debugfs_slab_add(struct kmem_cache *s) { } #endif背景:/sys/kernel/debug/slab/
debugfs 提供更偏调试的接口(sysfs 是稳定 ABI,debugfs 不是)。SLUB 在 debugfs 下创建条目用于:
导出更详细的调试信息
提供运行时调试控制(如动态开启/关闭某些缓存的调试)
debugfs_slab_add()就是为缓存创建 debugfs 条目。
条件
需要同时满足:
CONFIG_DEBUG_FS:内核编译了 debugfs 支持。CONFIG_SLUB_DEBUG:编译了 SLUB 调试支持。
任一不满足,就退化为空的内联桩函数。和 sysfs 一样,调用点无需#ifdef。
两者的对称结构
| 接口 | 条件 | 支持时 | 不支持时 |
|---|---|---|---|
sysfs_slab_add/sysfs_slab_alias | SLAB_SUPPORTS_SYSFS | 真函数 | 返回 0 的 inline 桩 |
debugfs_slab_add | CONFIG_DEBUG_FS && CONFIG_SLUB_DEBUG | 真函数 | 空 inline 桩 |
这种对称性说明 SLUB 同时维护两套用户/调试可见接口:sysfs 面向稳定观测和调优,debugfs 面向深度调试。
四、几句话串联
kmem_cache_create() └─ sysfs_slab_add(s) // 创建 /sys/kernel/slab/<name>/ └─ debugfs_slab_add(s) // 创建 debugfs 条目(仅调试配置) 分配/释放对象(开启 SLAB_STORE_USER) └─ set_track(s, obj, TRACK_ALLOC / TRACK_FREE) └─ struct track { addr, handle, cpu, pid, when }track_item和struct track是调试追踪的一部分,记录对象生命周期。sysfs_slab_add/debugfs_slab_add是观测接口,把缓存信息暴露给用户空间。
五、一句话总结
enum track_item { TRACK_ALLOC, TRACK_FREE }:标记struct track记录的是分配还是释放事件,用于对象生命周期追踪。sysfs 部分:支持
SLAB_SUPPORTS_SYSFS时用真函数注册/sys/kernel/slab/条目和别名,否则用返回 0 的 inline 桩,消除调用点的条件编译。debugfs 部分:仅在
CONFIG_DEBUG_FS && CONFIG_SLUB_DEBUG时提供debugfs_slab_add(),否则空桩。