设为首页 加入收藏

TOP

深入理解 slab cache 内存分配全链路实现(五)
2023-07-23 13:32:28 】 浏览:174
Tags:slab cache 全链路
逻辑 static inline void *new_slab_objects(struct kmem_cache *s, gfp_t flags, int node, struct kmem_cache_cpu **pc) { // 从 numa node cache 中获取到的空闲对象列表 void *freelist; // slab cache 本地 cpu 缓存 struct kmem_cache_cpu *c = *pc; // 分配对象所在的内存页 struct page *page; // 尝试从指定的 node 节点缓存 kmem_cache_node 中的 partial 列表获取可以分配空闲对象的 slub // 如果指定 numa 节点的内存不足,则会根据 cpu 访问距离的远近,进行跨 numa 节点分配 freelist = get_partial(s, flags, node, c); if (freelist) // 返回 numa cache 中缓存的空闲对象列表 return freelist; // 流程走到这里说明 numa cache 里缓存的 slub 也用尽了,无法找到可以分配对象的 slub 了 // 只能向底层伙伴系统重新申请内存页(slub),然后从新的 slub 中分配对象 page = new_slab(s, flags, node); // 将新申请的内存页 page (slub),缓存到 slab cache 的本地 cpu 缓存中 if (page) { // 获取 slab cache 的本地 cpu 缓存 c = raw_cpu_ptr(s->cpu_slab); // 刷新本地 cpu 缓存,将旧的 slub 缓存与 cpu 本地缓存解绑 if (c->page) flush_slab(s, c); // 将新申请的 slub 与 cpu 本地缓存绑定,page->freelist 赋值给 kmem_cache_cpu->freelist freelist = page->freelist; // 绑定之后 page->freelist 置空 // 现在新的 slub 中的空闲对象就已经缓存再了 slab cache 的本地 cpu 缓存中,后续就直接从这里分配了 page->freelist = NULL; stat(s, ALLOC_SLAB); // 将新申请的 slub 对应的 page 赋值给 kmem_cache_cpu->page c->page = page; *pc = c; } // 返回空闲对象列表 return freelist; }

内核首先会在 get_partial 函数中找到我们指定的 NUMA 节点缓存结构 kmem_cache_node ,然后开始遍历 kmem_cache_node->partial 链表直到找到一个可供分配对象的 slab。然后将这个 slab 提升为 slab cache 的本地 cpu 缓存,并从 kmem_cache_node->partial 链表中依次填充 slab 到 kmem_cache_cpu->partial。

image

如果我们指定的 NUMA 节点 kmem_cache_node->partial 链表也是空的,随后内核就会跨 NUMA 节点进行查找,按照访问距离由近到远,开始查找其他 NUMA 节点 kmem_cache_node->partial 链表。

如果还是不行,最后就只能通过 new_slab 函数到伙伴系统中重新申请一个 slab,并将这个 slab 提升为本地 cpu 缓存。

3.2.1 从 NUMA 节点缓存 partial 链表中查找

static void *get_partial(struct kmem_cache *s, gfp_t flags, int node,
        struct kmem_cache_cpu *c)
{
    // 从指定 node 的 kmem_cache_node 缓存中的 partial 列表中获取到的对象
    void *object;
    // 即将要所搜索的 kmem_cache_node 缓存对应 numa node
    int searchnode = node;
    // 如果我们指定的 numa node 已经没有空闲内存了,则选取访问距离最近的 numa node 进行跨节点内存分配
    if (node == NUMA_NO_NODE)
        searchnode = numa_mem_id();
    else if (!node_present_pages(node))
        searchnode = node_to_mem_node(node);

    // 从 searchnode 的 kmem_cache_node 缓存中的 partial 列表中获取对象
    object = get_partial_node(s, get_node(s, searchnode), c, flags);
    if (object || node != NUMA_NO_NODE)
        return object;
    // 如果 searchnode 对象的 kmem_cache_node 缓存中的 partial 列表是空的,没有任何可供分配的 slub
    // 那么继续按照访问距离,遍历 searchnode 之后的 numa node,进行跨节点内存分配
    return get_any_partial(s, flags, c);
}

get_partial 函数的主要内容是选取合适的 NUMA 节点缓存,优先使用我们指定的 NUMA 节点,如果指定的 NUMA 节点中没有足够的内存,内核就会跨 NUMA 节点按照访问距离的远近,选取一个合适的 NUMA 节点。

然后通过 get_partial_node 在选取的 NUMA 节点缓存 kmem_cache_node->partial 链表中查找 slab。

/*
 * Try to allocate a partial slab from a specific node.
 */
static void *get_partial_node(struct kmem_cache *s, struct kmem_cache_node *n,
                struct kmem_cache_cpu *c, gfp_t flags)
{
    // 接下来就会挨个遍历 kmem_cache_node 的 partial 列表中的 slub
    // 这两个变量用于临时存储遍历的 slub
    struct page *page, *page2;
    // 用于指向从 partial 列表 slub 中申请到的对象
    void *object = NULL;
    // 用于记录 slab cache 本地 cpu 缓存 kmem_cache_cpu 中所缓存的空闲对象总数(包括 partial 列表)
    // 后续会向 kmem_cache_cpu 中填充 slub
    unsigned int available = 0;
    // 临时记录遍历到的 slub 中包含的剩余空闲对象个数
    int objects;

    spin_lock(&n->list_lock);
    // 开始挨个遍历 kmem_cache_node 的 partial 列表,获取 slub 用于分配对象以及填充 kmem_cache_cpu
    list_for_each_entry_safe(page, page2, &n->partial, slab_list) {
        void *t;
        // page 表示当前遍历到的 slub,这里会从该 slub 中获取空闲对象赋值给 t
        // 并将 slub 从 kmem_cache_node 的 partial 列表上摘下
        t = acq
首页 上一页 2 3 4 5 6 7 8 下一页 尾页 5/10/10
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇在docker 环境中 websocket 通过n.. 下一篇关于 Bash 脚本中 Shebang 的趣事

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目