
SLUB 统计计数的递增辅助函数配合enum stat_item和kmem_cache_cpu.stat[]使用。核心设计点在于“per-CPU 无锁、容忍轻微不精确、避免关中断开销”。一、stat()单次递增static inline void stat(const struct kmem_cache *s, enum stat_item si) { #ifdef CONFIG_SLUB_STATS /* * The rmw is racy on a preemptible kernel but this is acceptable, so * avoid this_cpu_add()s irq-disable overhead. */ raw_cpu_inc(s-cpu_slab-stat[si]); #endif }功能把缓存s的本 CPU 统计数组里第si项加 1。调用形式stat(s, ALLOC_FASTPATH); stat(s, FREE_SLOWPATH); stat(s, CPU_PARTIAL_DRAIN);关键raw_cpu_inc而非this_cpu_inc这是注释的核心。区别操作是否关中断/抢占是否原子开销this_cpu_inc()关中断或至少保证不迁移单 CPU 上是原子的较大raw_cpu_inc()不关中断、不关抢占非原子可能出现 RMW 竞争极小this_cpu_*系列会保证操作期间不被中断/抢占打断防止read-modify-writeRMW读-改-写被并发打断从而在单 CPU 视角下是原子的。raw_cpu_*系列不做任何保护就是一条inc指令读内存、加 1、写回。在可抢占内核上这条指令可能被抢占打断别的代码可能同时改同一个计数器导致丢失更新。注释明确说“The rmw is racy on a preemptible kernel but this is acceptable”——在可抢占内核上这个 RMW 有竞争但可以接受。原因统计值用于性能分析不是正确性关键数据偶尔少算几次无所谓。关中断/关抢占的代价尤其在高频分配路径上比统计精度更重要。计数器是per-CPU的跨 CPU 不会冲突只有本 CPU 被抢占时才可能丢更新概率低、影响小。所以这里刻意选择raw_cpu_inc用统计精度换分配/释放路径的极致开销。条件编译#ifdef CONFIG_SLUB_STATS ... #endif未开启CONFIG_SLUB_STATS时函数体为空stat()调用完全被优化掉零开销。配合kmem_cache_cpu.stat[]字段也是条件编译的两者一致。二、stat_add()批量/带值增加static inline void stat_add(const struct kmem_cache *s, enum stat_item si, int v) { #ifdef CONFIG_SLUB_STATS raw_cpu_add(s-cpu_slab-stat[si], v); #endif }功能把第si项加vv可以是任意整数不只 1。用途举例// 一次处理 n 个对象批量计数 stat_add(s, FREE_SLAB, 1); // 或某些场景一次补多个 stat_add(s, ALLOC_REFILL, nr);同样用raw_cpu_addraw_cpu_add是raw_cpu_inc的带值版本同样不关中断、不关抢占、非原子。注释理由与stat()相同可接受轻微竞争换取低开销。未开CONFIG_SLUB_STATS时同样编译为空。为什么需要stat_add而不只有stat有些事件一次涉及多个对象或多次操作逐个stat()调用不如一次stat_add(..., v)高效也避免循环里的函数调用。典型如一次补充 freelist 时补了nr个对象 →ALLOC_REFILL nr一次 drain 多个 slab →CPU_PARTIAL_DRAIN n三、两者对比stat()stat_add()增量固定 1v底层操作raw_cpu_incraw_cpu_add场景单次事件计数批量/带值计数保护无raw无raw条件编译CONFIG_SLUB_STATSCONFIG_SLUB_STATS精度可抢占下可能丢失更新同左四、相关的呼应呼应这里体现enum stat_itemsi参数就是它的枚举值kmem_cache_cpu.stat[NR_SLUB_STAT_ITEMS]s-cpu_slab-stat[si]访问的就是它CONFIG_SLUB_STATS控制整个统计机制是否编译进来per-CPU 无锁设计统计也放 per-CPU避免全局原子竞争USE_LOCKLESS_FAST_PATH()等开关同样体现“正常路径追求零开销精度可妥协”一个有意思的对比前面的USE_LOCKLESS_FAST_PATH()在 RT 下放弃无锁以保证正确性而这里的统计在可抢占内核下接受竞争以保住性能。两者取舍方向相反但都是基于“该数据是否影响正确性”来判断freelist 操作错误 → 内存损坏必须保证正确 → RT 下加锁。统计计数偏差 → 只影响分析数字可接受 → 用 raw 操作省开销。五、raw_cpu_*与this_cpu_*的选用原则内核通用场景选择数据是正确性关键不能被并发打断this_cpu_*隐含关中断/抢占数据是统计/近似值允许竞争raw_cpu_*省开销已在关抢占/关中断上下文中raw_cpu_*也可安全用