slub node_nr_slabs
node_nr_slabs()是 SLUB 调试里用于读取一个 NUMA 节点上该缓存的 slab 页总数的访问器封装了atomic_long_read()。一、函数签名static inline unsigned long node_nr_slabs(struct kmem_cache_node *n) { return atomic_long_read(n-nr_slabs); }参数含义nper-NUMA-node 的struct kmem_cache_node返回该节点上该缓存的 slab 页总数unsigned long。二、n-nr_slabs回顾struct kmem_cache_nodestruct kmem_cache_node { spinlock_t list_lock; unsigned long nr_partial; struct list_head partial; #ifdef CONFIG_SLUB_DEBUG atomic_long_t nr_slabs; // ← 这里 atomic_long_t total_objects; struct list_head full; #endif };仅在CONFIG_SLUB_DEBUG下存在。记录该节点上该缓存的所有 slab 页数量partial full 其他。用atomic_long_t而非普通unsigned long因为可能在不加list_lock的路径被读取或更新如统计输出、validate_slab_cache需要原子性。三、为什么用atomic_long_read()return atomic_long_read(n-nr_slabs);atomic_long_read()原子读取保证不会读到撕裂值如 64 位变量在 32 位系统上被部分更新。虽然写入通常也在锁内但读取可能发生在sysfs/debugfs 统计输出。kmem_cache_shrink()计算。内存热插拔回调。这些路径不一定持有list_lock所以用原子读保证一致性。四、配套的写操作nr_slabs的更新通常用原子加减// slab 加入 static inline void inc_slabs_node(struct kmem_cache *s, int node, int objects) { struct kmem_cache_node *n get_node(s, node); atomic_long_inc(n-nr_slabs); atomic_long_add(objects, n-total_objects); } // slab 移除 static inline void dec_slabs_node(struct kmem_cache *s, int node, int objects) { struct kmem_cache_node *n get_node(s, node); atomic_long_dec(n-nr_slabs); atomic_long_sub(objects, n-total_objects); }调用时机allocate_slab()新 slab 从伙伴系统分配后inc_slabs_node()。discard_slab()slab 归还伙伴系统时dec_slabs_node()。五、与total_objects的配套static inline unsigned long node_nr_objs(struct kmem_cache_node *n) { return atomic_long_read(n-total_objects); }node_nr_slabs()和node_nr_objs()成对分别读函数字段含义node_nr_slabsnr_slabsslab 页数node_nr_objstotal_objects对象总数nr_slabs × objects_per_slab用于计算使用率、空闲率、内存占用等。六、使用场景node_nr_slabs()在以下路径被调用1. sysfs / debugfs 统计/sys/kernel/slab/cache/slabs输出该缓存所有节点的nr_slabs之和。2.kmem_cache_shrink()static unsigned long count_partial(struct kmem_cache_node *n, ...) { ... return node_nr_slabs(n); // 或遍历 partial 计算 }判断是否需要收缩。3.validate_slab_cache()static void validate_slab_cache(struct kmem_cache *s) { int node; struct kmem_cache_node *n; for_each_kmem_cache_node(s, node, n) { pr_info(Node %d, slabs %lu, objects %lu\n, node, node_nr_slabs(n), node_nr_objs(n)); } }调试时汇总每个节点的 slab 和对象数。4. 内存热插拔节点上线/下线时更新和检查这些计数。七、知识呼应相关函数这里体现struct kmem_cache_noden-nr_slabs字段atomic_long_t原子读写避免锁外撕裂inc_slabs_node/dec_slabs_node写侧操作get_node()获取n的方式for_each_kmem_cache_node遍历所有节点读取计数CONFIG_SLUB_DEBUG字段和函数的存在条件八、总结node_nr_slabs(n)用atomic_long_read(n-nr_slabs)原子读取一个 NUMA 节点上该缓存的 slab 页总数仅在CONFIG_SLUB_DEBUG下存在。它与node_nr_objs()配套用于 sysfs 统计、kmem_cache_shrink()判断和validate_slab_cache()汇总写侧由inc_slabs_node()/dec_slabs_node()维护。