slub get_node SLUB 里访问per-NUMA-node 结构的最基础辅助函数一行代码但背后涉及 SLUB 的节点组织和编译配置。一、函数本身static inline struct kmem_cache_node *get_node(struct kmem_cache *s, int node) { return s-node[node]; }功能给定缓存s和 NUMA 节点号node返回该缓存在这个节点上的struct kmem_cache_node。调用形式struct kmem_cache_node *n get_node(s, node);get_partial()、__slab_free()、add_partial()、discard_slab()等慢路径函数第一步几乎都是拿n然后在n-list_lock保护下操作n-partial等。二、s-node是什么在struct kmem_cache里有struct kmem_cache_node *node[MAX_NUMNODES];这是一个按节点号索引的指针数组每个元素指向该缓存的对应节点的kmem_cache_node。MAX_NUMNODES由CONFIG_NODES_SHIFT决定非 NUMA 系统通常为 1NUMA 系统可达 64、1024 等。所以get_node(s, node)本质就是数组下标访问O(1)、无锁、无分支。三、为什么用static inline极高频调用慢路径每一步都要用而且常作为参数传递。极简单只有一次数组访问函数调用开销相对过大。内联后编译器可把它折叠进调用点甚至把s-node[node]直接编进后续代码消除中间变量。这是内核里“简单访问器一律内联”的典型做法。四、与 NUMA 配置的关系SLUB 的node数组组织随 NUMA 配置变化配置MAX_NUMNODESs-node[]非 NUMACONFIG_NUMA关1只有一个元素node恒为 0NUMACONFIG_NUMA开多每个节点一项因此get_node(s, node)在非 NUMA 下等价于s-node[0]编译器可能把整个节点参数优化掉。SLUB 代码不需要为此写两套靠这个访问器屏蔽差异。五、与strict_numa的呼应#ifdef CONFIG_NUMA static DEFINE_STATIC_KEY_FALSE(strict_numa); #endif默认strict_numa关get_node()拿到的节点可能与请求节点不同SLUB 允许跨节点取 slab优先保证分配成功。开启strict_numa分配时严格要求从请求节点的kmem_cache_node取get_node(s, node)里的node必须是目标节点不匹配就可能失败或走特殊路径。ALLOC_NODE_MISMATCH统计的就是“实际 slab 所在节点 ≠ 请求节点”的情况判断依据正是通过get_node()拿到的 node 信息。六、对比this_cpu_ptrSLUB 的两类后端访问方式形成对称访问粒度方式路径this_cpu_ptr(s-cpu_slab)per-CPU隐式当前 CPU快速路径get_node(s, node)per-node显式传node慢路径per-CPU 用this_cpu_ptr因为“当前 CPU”是隐式的且要配合preempt_disable()/migrate_disable()。per-node 用get_node(s, node)因为节点号是显式参数可能来自numa_node_id()、请求参数、对象所在页等。七、使用示例慢路径骨架static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, ...) { struct kmem_cache_node *n; ... n get_node(s, node); // 拿到目标节点的 node 结构 spin_lock(n-list_lock); slab get_partial(s, n, ...); // 从 n-partial 找 spin_unlock(n-list_lock); ... }static void __slab_free(struct kmem_cache *s, struct slab *slab, void *object, ...) { struct kmem_cache_node *n; ... n get_node(s, slab_nid(slab)); // 用 slab 实际所在节点 spin_lock(n-list_lock); ... spin_unlock(n-list_lock); }注意两者传的node来源不同分配node是请求节点可能来自kmalloc_node参数或numa_node_id()。释放node是对象所在 slab 的实际节点slab_nid(slab)因为释放必须回到对象真正的归属节点。八、一句话总结get_node(s, node)是 SLUB 访问per-NUMA-node 结构的访问器s-node[node]一次数组下标static inline保证零开销。它屏蔽了 NUMA 与非 NUMA 的差异是慢路径拿到list_lock和partial列表的入口。