一、概述
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 | size | type | remark |
kmalloc-{size} | 8Byte~64M | KMALLOC_NORMAL | |
kmalloc-rcl-{size} | 8Byte~64M | KMALLOC_RECLAIM | |
dma-kmalloc-{size} | 8Byte~64M | KMALLOC_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;
}