ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

slub get_node

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 系统通常为 1,NUMA 系统可达 64、1024 等。

所以get_node(s, node)本质就是数组下标访问,O(1)、无锁、无分支。

三、为什么用static inline

  • 极高频调用:慢路径每一步都要用,而且常作为参数传递。

  • 极简单:只有一次数组访问,函数调用开销相对过大。

  • 内联后编译器可把它折叠进调用点,甚至把s->node[node]直接编进后续代码,消除中间变量。

这是内核里“简单访问器一律内联”的典型做法。

四、与 NUMA 配置的关系

SLUB 的node数组组织随 NUMA 配置变化:

配置MAX_NUMNODESs->node[]
非 NUMA(CONFIG_NUMA关)1只有一个元素,node恒为 0
NUMA(CONFIG_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_ptr

SLUB 的两类后端访问方式形成对称:

访问粒度方式路径
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列表的入口。

返回列表