欢迎光临
我们一直在努力

slub add_full

add_full() 是 SLUB 调试中用于把完全占满的 slab 页加入 per-node 的 full 链表的函数。这个函数只在 CONFIG_SLUB_DEBUG 下存在,因为正常运行时 SLUB 不追踪满 slab。

一、函数签名

static void add_full(struct kmem_cache *s,
struct kmem_cache_node *n, struct slab *slab)

参数含义
s 缓存
n 对象所在 NUMA 节点的 kmem_cache_node
slab 要加入的 slab 页

无返回值。

二、典型实现

static void add_full(struct kmem_cache *s,
struct kmem_cache_node *n, struct slab *slab)
{
if (!(s->flags & SLAB_STORE_USER))
return;

lockdep_assert_held(&n->list_lock);
list_add(&slab->slab_list, &n->full);
}

逻辑:

  • 门控:未开 SLAB_STORE_USER 直接返回。

  • 锁断言:lockdep_assert_held(&n->list_lock) 确认调用者已持有 node 的 list_lock。

  • 头插链表:list_add(&slab->slab_list, &n->full) 把 slab 加入 full 列表头部。

  • 三、full 列表回顾

    struct kmem_cache_node:

    struct 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
    };

    • partial:部分空闲的 slab,正常运行时也维护。

    • full:完全占满的 slab,仅调试时维护。

    为什么正常运行时不需要 full:

    • 满 slab 没有空闲对象,分配路径不会用到。

    • 释放时若某个满 slab 变部分空闲,需要知道它在哪——正常运行时它根本不在任何列表上,靠对象指针反查 slab 即可处理。

    • 只有调试时需要枚举所有 slab(校验、统计),才需要 full 列表把满 slab 也挂起来。

    四、调用时机

    add_full() 在 slab 由非满变满时调用:

    // 分配路径中,对象从 slab 取出,slab 变满
    if (slab->inuse == slab->objects) {
    // 从 partial 移除,加入 full
    remove_partial(n, slab);
    add_full(s, n, slab);
    }

    对应地,由满变非满时调 remove_full():

    static void remove_full(struct kmem_cache *s, struct kmem_cache_node *n,
    struct slab *slab)
    {
    if (!(s->flags & SLAB_STORE_USER))
    return;

    lockdep_assert_held(&n->list_lock);
    list_del(&slab->slab_list);
    }

    状态变化操作
    partial → full remove_partial + add_full
    full → partial remove_full + add_partial
    full → free remove_full + 归还伙伴系统

    五、与其他列表操作的关系

    SLUB 里 slab 列表操作的完整集合:

    函数操作
    add_partial(n, slab, tail) 加入 partial 列表(头或尾)
    remove_partial(n, slab) 从 partial 移除
    add_full(s, n, slab) 加入 full 列表(仅调试)
    remove_full(s, n, slab) 从 full 移除(仅调试)

    这些操作都在 n->list_lock 保护下进行,且通常成对出现。

    六、锁与并发

    lockdep_assert_held(&n->list_lock);

    • lockdep_assert_held():调试断言,确认调用者持有指定锁。

    • 不改变运行时行为(未开 lockdep 时是空宏),但能在开发阶段抓出“忘了加锁”的 bug。

    • 所有列表操作都必须在 list_lock 保护下,因为 n->partial / n->full 是多个 CPU 共享的 per-node 结构。

    调用者通常:

    spin_lock(&n->list_lock);
    // … 操作 slab 状态
    add_full(s, n, slab);
    spin_unlock(&n->list_lock);

    七、slab_list 字段

    list_add(&slab->slab_list, &n->full);

    • slab->slab_list:struct slab 里的 struct list_head 成员,用于挂在各种列表上。

    • 同一个 slab_list 字段在不同状态下挂不同列表:

      • 在 partial 列表上 → 挂在 n->partial。

      • 在 full 列表上 → 挂在 n->full。

      • 在 CPU partial 上 → 挂在 c->partial。

    • 因为一个 slab 同一时刻只能处于一种列表状态,复用同一个 list_head 节省空间。

    八、与前面知识的呼应

    前面讲过这里体现
    struct kmem_cache_node n->full、n->list_lock
    SLAB_STORE_USER 门控条件
    list_lock 保护列表操作
    add_partial / remove_partial 对称的 partial 操作
    struct slab slab->slab_list 字段
    slab->inuse / slab->objects 判断满/非满

    九、总结

    add_full(s, n, slab) 在开启 SLAB_STORE_USER 时,把由非满变满的 slab 页加入 per-node 的 full 链表,要求在 n->list_lock 保护下调用。它只在调试时存在,用于维护完整视图以便校验和统计;正常运行时满 slab 不被追踪,以节省开销。

    赞(0)
    未经允许不得转载:171主机测评 » slub add_full
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址