linux5.2.11 内存管理-slab机制详解(一)-初始化

一、概述

Slab分配器的初始化在kmem_cache_init()中进行,

内核调用路径: start_kernel()->mm_init()->kmem_cache_init()

/* slab分配器中的SLAB高速缓存 */
struct kmem_cache {
    /* 指向包含空闲对象的本地高速缓存,每个CPU有一个该结构,当有对象释放时,优先放入本地CPU高速缓存中 */
    struct array_cache __percpu *cpu_cache;

     ............暂时忽略其他成员..................

    /* 高速缓存中对象的大小 */
    int object_size;

    /* 要转移进本地高速缓存或从本地高速缓存中转移出去的对象的数量 */
    unsigned int batchcount;

    /* 结点链表,此高速缓存可能在不同NUMA的结点都有SLAB链表 */
    struct kmem_cache_node *node[MAX_NUMNODES];
}

二、引子

(一)使用slab分配器申请内存的工作机制

1)首先根据要申请的内存大小 size 去找到相应的 object_size 与之相近的高速缓存(struct kmem_cache),假设为kmem_cache_A;

2)然后从本地空闲object链表(kmem_cache_A->cpu_cache )分配一个空闲object;

3)如果本地cpu_cache没有空闲的了,则尝试来到所有CPU共享的空闲object链表(kmem_cache_A->node[nodeId]->shared)尝试迁移一部分(最多batchcount个)空闲object 到本地空闲object 链表;

4)如果还是失败,则从本地节点slab链表(kmem_cache_A->node[nodeId]->slabs_partial/slabs_free)有空闲object的slab中迁移一部分(最多batchcount个)空闲object 到本地空闲object 链表;

5)如果失败,则从伙伴系统申请页框建立一个新的slab加入kmem_cache_A->node[nodeId]->slabs_partial/slabs_free,然后从该空闲slab中迁移一部分(最多batchcount个)空闲object 到本地空闲object 链表;

6)如果迁移成功,最后从从本地空闲object链表(kmem_cache_A->cpu_cache )分配一个空闲object, 申请成功;如果迁移失败,则内存申请失败。

(二)先有鸡还是先有蛋?

由上工作机制得知,基于页框分配器(伙伴系统),要使得 slab分配器正常工作,首先要创建对应的 struct kmem_cache , 然后创建 struct kmem_cache中通过struct array_cache *cpu_cache和 struct kmem_cache_node *node[]分别指向的struct array_cache 和 struct kmem_cache_node ,即:

1)问题1:创建 struct kmem_cache,需要通过 slab分配器去分配;

2)创建 struct array_cache ,由于是每CPU数组,是通过每个cpu保留的内存中去分配,本文暂且不关注;

3)问题2:创建struct kmem_cache_node, 需要通过 slab分配器去分配。

问题来了,在slab分配器还没初始化的时候,我们是不能通过 slab分配器去分配内存的,也就没法通过slab分配器去创建 struct kmem_cache和 struct kmem_cache_node。这就是鸡和蛋的问题了。解决方法如下:

三、kmem_cache_init() 工作流程

1)解决问题1:通过全局变量(这里是 kmem_cache_boot)的方式,静态创建第一个 struct kmem_cache 实例,然后 将它的 struct kmem_cache ->node[] 也指向 静态的 struct kmem_cache_node  (这里是 init_kmem_cache_node[] 数组),如此第一个 struct kmem_cache 实例便初始化好了。需要特别指出的是,它的object_size 正是 struct kmem_cache 结构体的大小(或大点),所以第一个 struct kmem_cache 实例可以专门用来动态申请内存来创建 struct kmem_cache 结构体。第一个轮子造好了~ 将它链入全局链表 slab_caches。

2)解决问题2:由于问题1解决了,我们可以动态申请内存创建 第二个 struct kmem_cache实例。由于还是没有用于 创建struct kmem_cache_node的实例,所以还是将第二个实例的 struct kmem_cache ->node[] 也指向 init_kmem_cache_node[]成员,如此第二个 struct kmem_cache 实例也初始化好了。相信你应该猜到了,它的object_size 正是 struct kmem_cache_node 结构体的大小(或大点),所以第二个 struct kmem_cache 实例可以专门用来动态申请内存来创建 struct kmem_cache_node 结构体。第二轮子也造好了,马车可以奔跑了~将它链入全局链表 slab_caches。

3)解决了 问题2 后,我们便可以动态申请struct kmem_cache_node 结构体,然后将第一、二个struct kmem_cache实例中指向静态init_kmem_cache_node[]成员 的struct kmem_cache ->node[] 替换掉,因为后面 init_kmem_cache_node[] 在内核初始化完后是要被释放掉的。

4)问题1 和 问题2 都解决了,接着我们可以动态创建其他的 linux 通用的 struct kmem_cache实例,然后链入全局链表 slab_caches。主要是根据 kmalloc_info[] 来进行创建,object size 为 8byte~64M , 命名 一般为3种

name        sizetyperemark
kmalloc-{size}  8Byte~64MKMALLOC_NORMAL
kmalloc-rcl-{size}  8Byte~64MKMALLOC_RECLAIM
dma-kmalloc-{size}8Byte~64MKMALLOC_DMA

如果打开CONFIG_ZONE_DMA

对应于每一个KMALLOC_NORMAL类型的都创建1个KMALLOC_DMA

const struct kmalloc_info_struct kmalloc_info[] __initconst = {
    {NULL,                      0}, {"kmalloc-96",             96},
    {"kmalloc-192",           192}, {"kmalloc-8",               8},
    {"kmalloc-16",             16}, {"kmalloc-32",             32},
    {"kmalloc-64",             64}, {"kmalloc-128",           128},
    {"kmalloc-256",           256}, {"kmalloc-512",           512},
    {"kmalloc-1k",           1024}, {"kmalloc-2k",           2048},
    {"kmalloc-4k",           4096}, {"kmalloc-8k",           8192},
    {"kmalloc-16k",         16384}, {"kmalloc-32k",         32768},
    {"kmalloc-64k",         65536}, {"kmalloc-128k",       131072},
    {"kmalloc-256k",       262144}, {"kmalloc-512k",       524288},
    {"kmalloc-1M",        1048576}, {"kmalloc-2M",        2097152},
    {"kmalloc-4M",        4194304}, {"kmalloc-8M",        8388608},
    {"kmalloc-16M",      16777216}, {"kmalloc-32M",      33554432},
    {"kmalloc-64M",      67108864}
};

四、重要数据类型及全局变量

重要数据类型

/* slab分配器中的SLAB高速缓存 */
struct kmem_cache {
    /* 指向包含空闲对象的本地高速缓存,每个CPU有一个该结构,当有对象释放时,优先放入本地CPU高速缓存中 */
    struct array_cache __percpu *cpu_cache;

/* 1) Cache tunables. Protected by slab_mutex */
    /* 要转移进本地高速缓存或从本地高速缓存中转移出去的对象的数量 */
    unsigned int batchcount;
    /* 本地高速缓存中空闲对象的最大数目 */
    unsigned int limit;
    /* 是否存在CPU共享高速缓存,CPU共享高速缓存指针保存在kmem_cache_node结构中 */
    unsigned int shared;

    /* 对象长度 + 填充字节 */
    unsigned int size;
    /* size的倒数,加快计算 */
    struct reciprocal_value reciprocal_buffer_size;

    
/* 2) touched by every alloc & free from the backend */
    /* 高速缓存永久属性的标识,如果SLAB描述符放在外部(不放在SLAB中),则CFLAGS_OFF_SLAB置1 */
    unsigned int flags;        /* constant flags */
    /* 每个SLAB中对象的个数(在同一个高速缓存中slab中对象个数相同) */
    unsigned int num;        /* # of objs per slab */


/* 3) cache_grow/shrink */
    /* 一个单独SLAB中包含的连续页框数目的对数 */
    unsigned int gfporder;

    /* 分配页框时传递给伙伴系统的一组标识 */
    gfp_t allocflags;

    /* SLAB使用的颜色个数 */
    size_t colour;            
    /* SLAB中基本对齐偏移,当新SLAB着色时,偏移量的值需要乘上这个基本对齐偏移量,理解就是1个偏移量等于多少个B大小的值 */
    unsigned int colour_off;    
    /* 空闲对象链表放在外部时使用,其指向的SLAB高速缓存来存储空闲对象链表 */
    struct kmem_cache *freelist_cache;
    /* 空闲对象链表的大小 */
    unsigned int freelist_size;

    /* 构造函数,一般用于初始化这个SLAB高速缓存中的对象 */
    void (*ctor)(void *obj);


/* 4) cache creation/removal */
    /* 存放高速缓存名字 */
    const char *name;
    /* 高速缓存描述符双向链表指针 */
    struct list_head list;
    int refcount;
    /* 高速缓存中对象的大小 */
    int object_size;
    int align;


/* 5) statistics */
    /* 统计 */
#ifdef CONFIG_DEBUG_SLAB
    unsigned long num_active;
    unsigned long num_allocations;
    unsigned long high_mark;
    unsigned long grown;
    unsigned long reaped;
    unsigned long errors;
    unsigned long max_freeable;
    unsigned long node_allocs;
    unsigned long node_frees;
    unsigned long node_overflow;
    atomic_t allochit;
    atomic_t allocmiss;
    atomic_t freehit;
    atomic_t freemiss;

    /* 对象间的偏移 */
    int obj_offset;
#endif /* CONFIG_DEBUG_SLAB */
#ifdef CONFIG_MEMCG_KMEM
    /* 用于分组资源限制 */
    struct memcg_cache_params *memcg_params;
#endif
    /* 结点链表,此高速缓存可能在不同NUMA的结点都有SLAB链表 */
    struct kmem_cache_node *node[MAX_NUMNODES];
};


/* SLAB链表结构 */
struct kmem_cache_node {
    /* 锁 */
    spinlock_t list_lock;

/* SLAB用 */
#ifdef CONFIG_SLAB
    /* 只使用了部分对象的SLAB描述符的双向循环链表 */
    struct list_head slabs_partial;    /* partial list first, better asm code */
    /* 不包含空闲对象的SLAB描述符的双向循环链表 */
    struct list_head slabs_full;
    /* 只包含空闲对象的SLAB描述符的双向循环链表 */
    struct list_head slabs_free;
    /* 高速缓存中空闲对象个数(包括slabs_partial链表中和slabs_free链表中所有的空闲对象) */
    unsigned long free_objects;
    /* 高速缓存中空闲对象的上限 */
    unsigned int free_limit;
    /* 下一个被分配的SLAB使用的颜色 */
    unsigned int colour_next;    /* Per-node cache coloring */
    /* 指向这个结点上所有CPU共享的一个本地高速缓存 */
    struct array_cache *shared;    /* shared per node */
    struct alien_cache **alien;    /* on other nodes */
    /* 两次缓存收缩时的间隔,降低次数,提高性能 */
    unsigned long next_reap;    
    /* 0:收缩  1:获取一个对象 */
    int free_touched;        /* updated without locking */
#endif

/* SLUB用 */
#ifdef CONFIG_SLUB
    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
#endif

};

下面是一些重要的全局变量:

/* The list of all slab caches on the system */
/* 这个链表管理所有的 struct kmem_cache */
struct list_head slab_caches;

/* The slab cache that manages slab cache information */
/* 指向第一个 struct kmem_cache 实例, 第一个 struct kmem_cache 实例的
   每个对象的大小为 struct kmem_cache 结构体的大小,专用于申请这类大小的结构体所用 
*/
struct kmem_cache * kmem_cache;

/* 存放所有动态申请的 struct kmem_cache 指针 */
struct kmem_cache *kmalloc_caches[NR_KMALLOC_TYPES][KMALLOC_SHIFT_HIGH + 1];

五、kmem_cache_init()代码分析

kmem_cache_init


kmem_cache_init {
    //第一个kmem_cache实例,指向静态定义
    kmem_cache = &kmem_cache_boot; 

    //初始化所有struct kmem_cache_node
    for (i = 0; i < NUM_INIT_LISTS; i++) 
        kmem_cache_node_init(&init_kmem_cache_node[i]);

    /** (一)创建第一个 kmem_cache 实例(从 kmem_cache_boot 静态创建) , object size 为 struct kmem_cache 的大小
                由于其 object size 为 struct kmem_cache  的大小,所以 第一个 kmem_cache 实例是专门用于 struct kmem_cache  结构体申请的缓存

        1) 调用 create_boot_cache() ->__kmem_cache_create()->set_objfreelist_slab_cache/set_off_slab_cache/set_on_slab_cache
                完成计算 kmem_cache -> gfporder , colour , colour_off, flags 及其他成员 初始化
    
        2) 调用 create_boot_cache() ->__kmem_cache_create()->setup_cpu_cache()-> alloc_kmem_cache_cpus->__alloc_percpu()->pcpu_alloc()
            完成 kmem_cache->cpu_cache  每cpu变量数组 的申请
    
        3) 调用 create_boot_cache() ->__kmem_cache_create()->setup_cpu_cache()-> set_up_node() 
            完成 kmem_cache->node[i] 指向全局变量数组 init_kmem_cache_node[CACHE_CACHE+i]的成员
    
        4) 调用 create_boot_cache() ->__kmem_cache_create()->setup_cpu_cache()
                完成 kmem_cache->cpu_cache  -> avail , limit, touched, batchcount 等成员初始化
    
        5) 将 第一个 kmem_cache实例 加入 全局 cache 链表 slab_caches
    */
    create_boot_cache(kmem_cache, "kmem_cache",offsetof(struct kmem_cache, node) +nr_node_ids * sizeof(struct kmem_cache_node *),SLAB_HWCACHE_ALIGN, 0, 0) {
        /*这里offsetof这句是计算struct      kmem_cache的大小, node 是最后一个成员,nr_node_ids 保存内存节点个数,UMA时为1,所以 node 偏移加上    nr_node_ids 个
        kmem_cache_node 指针的大小 即为struct kmem_cache的大小*/
        __kmem_cache_create(struct kmem_cache *cachep, slab_flags_t flags){

            //*****__kmem_cache_create 关键步骤1*****: 计算并设置 struct kmem_cache中的成员 gfporder , colour , colour_off, flags等,
            //并确定 freelist 的存储方式 objfreelist off-slab on-slab
            set_objfreelist_slab_cache/set_off_slab_cache/set_on_slab_cache (cachep, size, flags){
                calculate_slab_order (cachep, size,flags ){
                    cache_estimate (gfporder, size, flags, &remainder)
                }
            }
            if (OFF_SLAB(cachep)) {// freelist off-slab 场景, freelist 都统一在 freelist_cache中申请,kmalloc_slab()获取相应的 kmem_cache 指针
                cachep->freelist_cache =kmalloc_slab(cachep->freelist_size, 0u);
            }
            setup_cpu_cache(cachep, gfp){//设置 struct kmem_cache的 kmem_cache_node* node[] 和 struct array_cache  __percpu *cpu_cache;
                //申请每cpu变量数组 kmem_cache->cpu_cache 所需的内存
                cachep->cpu_cache = alloc_kmem_cache_cpus(cachep, 1, 1){
                                                            __alloc_percpu (size, sizeof(void *)){
                                                                pcpu_alloc  (size, align, false, GFP_KERNEL)
                                                                    chunk = pcpu_create_chunk(pcpu_gfp)
                                                                    pcpu_alloc_area(chunk, bits, bit_align, off)
                                                                    pcpu_stats_area_alloc(chunk, size)
                                                                    pcpu_populate_chunk(chunk, rs, re, pcpu_gfp)
                                                                    pcpu_chunk_populated(chunk, rs, re)
                                                                    ptr = __addr_to_pcpu_ptr(chunk->base_addr + off)
                                                                    return ptr
                                                            }
                                                        }
                if (slab_state == DOWN) { /* Creation of first cache (kmem_cache). */
                    set_up_node(kmem_cache, CACHE_CACHE){
                        for_each_online_node(node) {
                            //*****__kmem_cache_create 关键步骤2*****:初始化 第一个 kmem_cache的node 指向 init_kmem_cache_node[CACHE_CACHE+i]的成员
                            kmem_cache->node[node] = &init_kmem_cache_node[CACHE_CACHE + node];
                        }
                    }
                } 
                //*****__kmem_cache_create 关键步骤3*****:初始化 struct kmem_cache结构体中成员 struct array_cache __percpu *cpu_cache;
                cpu_cache_get(cachep)->avail = 0;
                cpu_cache_get(cachep)->limit = BOOT_CPUCACHE_ENTRIES;
                cpu_cache_get(cachep)->batchcount = 1;
                cpu_cache_get(cachep)->touched = 0;
            }
        }
    }

    /* 将 第一个 kmem_cache实例 加入 全局 cache 链表 slab_caches*/
    list_add(&kmem_cache->list, &slab_caches);
    
    /**设置 slab_state 状态为 PARTIAL 在后面 setup_cpu_cache 会用到, 如上 */
    slab_state = PARTIAL;

    /** (二)创建第二个kmem_cache 实例(第一个动态创建的 kmem_cache 实例),  object size 为 struct kmem_cache_node 的大小
                由于其 object size 为 struct kmem_cache_node 的大小,所以 第二个 kmem_cache 实例 是专门用于 struct kmem_cache_node 结构体申请的缓存

    1) 调用 kmem_cache_zalloc()->kmem_cache_alloc()-> slab_alloc()
            从第一个 kmem_cache 实例中获取 一个 object  创建 struct kmem_cache 并清零

    2) 调用 create_boot_cache() ->__kmem_cache_create()->set_objfreelist_slab_cache/set_off_slab_cache/set_on_slab_cache
            完成计算 kmem_cache -> gfporder , colour , colour_off, flags 及其他成员 初始化

    3) 调用 create_boot_cache() ->__kmem_cache_create()->setup_cpu_cache()-> alloc_kmem_cache_cpus->__alloc_percpu()->pcpu_alloc()
        完成 kmem_cache->cpu_cache  每cpu变量数组 的申请

    4) 调用 create_boot_cache() ->__kmem_cache_create()->setup_cpu_cache()-> set_up_node() 
        完成 kmem_cache->node[i] 指向全局变量数组 init_kmem_cache_node[SIZE_NODE+i]的成员

    5) 调用 create_boot_cache() ->__kmem_cache_create()->setup_cpu_cache()
            完成 kmem_cache->cpu_cache  -> avail , limit, touched, batchcount 等成员初始化

    6) 将 第二个 kmem_cache实例 加入 全局 cache 链表 slab_caches

    */
    kmalloc_caches[KMALLOC_NORMAL][INDEX_NODE]= 
            create_kmalloc_cache (kmalloc_info[INDEX_NODE].name,kmalloc_size(INDEX_NODE), ARCH_KMALLOC_FLAGS,0, kmalloc_size(INDEX_NODE)){
            // INDEX_NODE = kmalloc_index(sizeof(struct kmem_cache_node)) ,故 对应的 cache 的 object size 大小为 sizeof(struct kmem_cache_node)
                // 从上面建立的第一个 struct kmem_cache 中 获取1个 object  用于创建第二个 struct kmem_cache
                struct kmem_cache *s =
                kmem_cache_zalloc(kmem_cache, GFP_NOWAIT){
                    kmem_cache_alloc(kmem_cache, GFP_NOWAIT | __GFP_ZERO)
                        slab_alloc(kmem_cache, GFP_NOWAIT | __GFP_ZERO, _RET_IP_) 
                        // 参考 slab_alloc 的注释,由于上面初始化 第一个 kmem_cache 时,它的 slab_full,slab_partial,slab_free 都被初始化为空,
                        // 所以这里 slab_alloc 应该会 向伙伴系统 申请页框 , 并返回 1个 object 给调用者 使用
                }
                
                // 参考上面
                create_boot_cache(s, kmalloc_info[INDEX_NODE].name, kmalloc_size(INDEX_NODE), ARCH_KMALLOC_FLAGS, 0,usersize,kmalloc_size(INDEX_NODE)){
                    __kmem_cache_create
                        set_objfreelist_slab_cache/set_off_slab_cache/set_on_slab_cache
                        setup_cpu_cache
                            cachep->cpu_cache = alloc_kmem_cache_cpus(cachep, 1, 1)
                            if (slab_state == PARTIAL) { /* For kmem_cache_node */
                                set_up_node(cachep, SIZE_NODE){
                                    for_each_online_node(node) {
                                        //初始化 cachep 的node指向 init_kmem_cache_node[SIZE_NODE+i]的成员
                                        cachep->node[node] = &init_kmem_cache_node[SIZE_NODE + node];
                                    }
                                }
                            }
                            cpu_cache_get(cachep)->avail = 0;
                            ......
                }
                // 将 第二个 kmem_cache实例 加入 全局 cache 链表 slab_caches
                list_add(&s->list, &slab_caches);
                return s;
            }

    /*设置 slab_state 状态为 PARTIAL_NODE 在后面 setup_cpu_cache 会用到, 如上 */
    slab_state = PARTIAL_NODE;

    setup_kmalloc_cache_index_table();

    /*设置 slab_early_init 从1 到 0,表示 slab early init 完成了,
    可以 动态申请  kmalloc(sizeof(struct kmem_cache )) 和 kmalloc(sizeof(struct kmem_cache_node))了 */
    slab_early_init = 0;

    /** (三)将 第一、二个 kmem_cache 实例 的 kmem_cache->node[MAX_NUMNODES]  
        更新指向到 从 第二个 kmem_cache 实例 动态申请的 kmem_cache_node 对象 */
    for_each_online_node(nid) {
        init_list(kmem_cache, &init_kmem_cache_node[CACHE_CACHE + nid], nid){
            //从 第二个 kmem_cache 实例动态申请新的 kmem_cache_node 对象 object
            ptr = kmalloc_node(sizeof(struct kmem_cache_node), GFP_NOWAIT, nodeid){
                __kmalloc_node
                    __do_kmalloc_node
                        cachep = kmalloc_slab(size, flags)
                        kmem_cache_alloc_node_trace(cachep, flags, node, size)
                            slab_alloc_node
            }
            
            // 旧的 kmem_cache_node  拷贝到 新的 kmem_cache_node 上来
            list=&init_kmem_cache_node[CACHE_CACHE + nid];
            memcpy(ptr, list, sizeof(struct kmem_cache_node));
            // 旧的kmem_cache_node的 slab 三链 拷贝到 新的 kmem_cache_node的 slab 三链 上来
            MAKE_ALL_LISTS(kmem_cache, ptr, nodeid);
                #define MAKE_ALL_LISTS(cachep, ptr, nodeid) \
                    do {    \
                            MAKE_LIST((cachep), (&(ptr)->slabs_full), slabs_full, nodeid);	\
                            MAKE_LIST((cachep), (&(ptr)->slabs_partial), slabs_partial, nodeid); \
                            MAKE_LIST((cachep), (&(ptr)->slabs_free), slabs_free, nodeid);\
                    } while (0)
                #define MAKE_LIST(cachep, listp, slab, nodeid)      \
                    do {    \
                        INIT_LIST_HEAD(listp);  \
                        list_splice(cachep->node[nodeid]->(slab), listp);\
                    } while (0)
            /* 将 第一个 kmem_cache 实例(用于 创建 struct kmem_cache 结构体  oject的 slab 的cache) 的 kmem_cache->node[MAX_NUMNODES] 
            更新指向到 上面从 第二个 kmem_cache 实例动态申请的 kmem_cache_node 对象*/
            kmem_cache->node[nodeid] = ptr;
        }
        init_list(kmalloc_caches[KMALLOC_NORMAL][INDEX_NODE],&init_kmem_cache_node[SIZE_NODE + nid], nid)//同上
    }

    /** (四)根据 kmalloc_info 创建剩下的 kmalloc 使用的 通用 kmem_cache 实例 object size 为 8byte~32M , 
    命名 一般为3种        kmalloc-{size}            kmalloc-rcl-{size}              dma-kmalloc-{size}
    其中 struct kmem_cache 从 第一个 kmem_cache 实例 动态分配
    其中 struct kmem_cache_node 从 第二个 kmem_cache 实例 动态分配
    */
    create_kmalloc_caches(ARCH_KMALLOC_FLAGS){
        for (type = KMALLOC_NORMAL; type <= KMALLOC_RECLAIM; type++) 
            for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) 
                new_kmalloc_cache(i, type, flags)/  new_kmalloc_cache(1, type, flags)/   new_kmalloc_cache(2, type, flags){
                    kmalloc_caches[type][i] = 
                        create_kmalloc_cache(name, kmalloc_info[i].size, flags, 0,kmalloc_info[i].size){
                            // 从上面建立的第一个 kmem_cache 中 获取1个 object  用于创建 struct kmem_cache
                            struct kmem_cache *s =
                                kmem_cache_zalloc(kmem_cache, GFP_NOWAIT){
                                    kmem_cache_alloc(kmem_cache, GFP_NOWAIT | __GFP_ZERO)
                                        slab_alloc(kmem_cache, GFP_NOWAIT | __GFP_ZERO, _RET_IP_) 
                                }
                            
                            // 参考上面 , 不同的是,可以从第二个 kmem_cache 实例 动态分配 struct kmem_cache_node 了
                            create_boot_cache(s, name, kmalloc_info[i].size, ARCH_KMALLOC_FLAGS, 0,0,kmalloc_info[i].size){
                                __kmem_cache_create
                                    set_objfreelist_slab_cache/set_off_slab_cache/set_on_slab_cache
                                    setup_cpu_cache
                                        cachep->cpu_cache = alloc_kmem_cache_cpus(cachep, 1, 1) // 申请每 cpu 数组 cpu_cache
                                        /* Remaining boot caches */
                                        for_each_online_node(node) {
                                            // 可以从第二个 kmem_cache 实例 动态分配 struct kmem_cache_node 了,不再使用 静态 的 init_kmem_cache_node[]
                                            cachep->node[node] = 
                                                kmalloc_node(sizeof(struct kmem_cache_node), gfp, node){
                                                    __kmalloc_node
                                                        __do_kmalloc_node
                                                            cachep = kmalloc_slab(size, flags)
                                                            kmem_cache_alloc_node_trace(cachep, flags, node, size)
                                                                slab_alloc_node
                                                                
                                                }
                                            kmem_cache_node_init(cachep->node[node]);// 初始化 struct kmem_cache_node
                                        }
                                        cpu_cache_get(cachep)->avail = 0;
                                        ......
                            }
                            // 将struct kmem_cache实例 加入 全局 cache 链表 slab_caches
                            list_add(&s->list, &slab_caches);
                            return s;
                        }
                }

        /* Kmalloc array is now usable */
        slab_state = UP;

       /* 对应每一个 Normal 类型的 cache 都创建一个 相应的 dma cache */
        #ifdef CONFIG_ZONE_DMA
        for (i = 0; i <= KMALLOC_SHIFT_HIGH; i++) {
            struct kmem_cache *s = kmalloc_caches[KMALLOC_NORMAL][i];
        
            if (s) {
                unsigned int size = kmalloc_size(i);
                const char *n = kmalloc_cache_name("dma-kmalloc", size);// dma-kmalloc-8k dma-kmalloc-16k  etc.
        
                kmalloc_caches[KMALLOC_DMA][i] = create_kmalloc_cache(n, size, SLAB_CACHE_DMA | flags, 0, 0);
            }
        }
        #endif
    }
}

setup_cpu_cache :申请设置每cpu数组 cpu_cache ,以及根据 slab_state 状态值 决定静态或动态申请 struct kmem_cache_node

setup_cpu_cache(cachep, gfp){//设置 struct kmem_cache的 kmem_cache_node* node[] 和 struct array_cache  __percpu *cpu_cache;
    //申请每cpu变量数组 kmem_cache->cpu_cache 所需的内存
    cachep->cpu_cache = alloc_kmem_cache_cpus(cachep, 1, 1){
                                                __alloc_percpu (size, sizeof(void *)){
                                                    pcpu_alloc  (size, align, false, GFP_KERNEL)
                                                        chunk = pcpu_create_chunk(pcpu_gfp)
                                                        pcpu_alloc_area(chunk, bits, bit_align, off)
                                                        pcpu_stats_area_alloc(chunk, size)
                                                        pcpu_populate_chunk(chunk, rs, re, pcpu_gfp)
                                                        pcpu_chunk_populated(chunk, rs, re)
                                                        ptr = __addr_to_pcpu_ptr(chunk->base_addr + off)
                                                        return ptr
                                                }
                                            }
    if (slab_state == DOWN) {
        /* Creation of first cache (kmem_cache). */
        set_up_node(kmem_cache, CACHE_CACHE){
            for_each_online_node(node) {
                //*****__kmem_cache_create 关键步骤2*****:初始化 第一个 kmem_cache的node 指向 init_kmem_cache_node[CACHE_CACHE+i]的成员
                kmem_cache->node[node] = &init_kmem_cache_node[CACHE_CACHE + node];
            }
        }
    } 
    else if (slab_state == PARTIAL) {
        /* For kmem_cache_node */
        set_up_node(cachep, SIZE_NODE){
            for_each_online_node(node) {
                //初始化 cachep 的node指向 init_kmem_cache_node[SIZE_NODE+i]的成员
                cachep->node[node] = &init_kmem_cache_node[SIZE_NODE + node];
            }
        }
    }
    else {
        /* Remaining boot caches */
        for_each_online_node(node) {
            cachep->node[node] = kmalloc_node(sizeof(struct kmem_cache_node), gfp, node);
            kmem_cache_node_init(cachep->node[node]);
        }
    }
    //*****__kmem_cache_create 关键步骤3*****:初始化 struct kmem_cache结构体中成员 struct array_cache __percpu *cpu_cache;
    cpu_cache_get(cachep)->avail = 0;
    cpu_cache_get(cachep)->limit = BOOT_CPUCACHE_ENTRIES;
    cpu_cache_get(cachep)->batchcount = 1;
    cpu_cache_get(cachep)->touched = 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值