3. kmem_cache初始化
/*初始化。在页面分配器已初始化以后且在smp_init()以前调用。*/ void __init kmem_cache_init(void) { int i; kmem_cache = &kmem_cache_boot; //第一个kmem_cache实例,静态定义 if (!IS_ENABLED(CONFIG_NUMA) || num_possible_nodes() == 1) use_alien_caches = 0; for (i = 0; i < NUM_INIT_LISTS; i++) kmem_cache_node_init(&init_kmem_cache_node[i]); //初始化静态定义的kmem_cache_node //这里有两个node实例;它们是分别为前两个kmem cache实例准备的,第一个kmem cache实例用于为建立其余kmem cache实例分配空间,第二个kmem cache实例用于为建立struct kmem_cache_node实例分配空间,因此前两个kmem cache实例须要静态分配struct kmem_cache_node实例。 /*低内存碎片抗性-若是未在命令行上覆盖,则仅在内存超过32MB的计算机上使用较大的页面顺序。*/ if (!slab_max_order_set && totalram_pages > (32 << 20) >> PAGE_SHIFT) slab_max_order = SLAB_MAX_ORDER_HI; /* 引导程序很棘手,由于从尚不存在的缓存中分配了多个对象: 1)初始化kmem_cache缓存:它包含全部缓存的kmem_cache结构,但kmem_cache自己除外:kmem_cache是静态分配的。最初,__ init数据区域用于头数组和kmem_cache_node结构,在引导程序末尾将其替换为kmalloc分配的数组。 2)建立第一个kmalloc缓存。新缓存的结构kmem_cache正常分配。 __init数据区用于头数组。 3)建立剩余的kmalloc缓存,并使用最小大小的头数组。 4)用kmalloc分配的数组替换kmem_cache的__init数据头数组和第一个kmalloc缓存。 5)将kmem_cache_node的__init数据替换为kmem_cache并另外一个具备kmalloc分配的内存的缓存。 6)将kmalloc缓存的头数组的大小调整为最终大小。*/ /* 1) 建立 kmem_cache */ /*struct kmem_cache 的大小取决于nr_node_ids & nr_cpu_ids*/ 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); list_add(&kmem_cache->list, &slab_caches); memcg_link_cache(kmem_cache, NULL); slab_state = PARTIAL; /*首先初始化为kmem_cache_node结构提供内存的缓存。没有这个,进一步的分配将会出错。*/ 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)); slab_state = PARTIAL_NODE; setup_kmalloc_cache_index_table(); slab_early_init = 0; /* 5) 替换引导程序kmem_cache_node */ { int nid; for_each_online_node(nid) { init_list(kmem_cache, &init_kmem_cache_node[CACHE_CACHE + nid], nid); init_list(kmalloc_caches[KMALLOC_NORMAL][INDEX_NODE], &init_kmem_cache_node[SIZE_NODE + nid], nid); } } create_kmalloc_caches(ARCH_KMALLOC_FLAGS); }
这个函数是从start_kernel()->mm_init()->kmem_cache_init()调用。node
在最开始,先拿到了kmem_cache_boot和init_kmem_cache_node这两个静态变量。第一个是用来存kmem_cache实例,第二个是有两个node实例,第一个用来为建立其余kmem_cache分配空间,第二个用于为建立kmem_cache_node分配空间。数组
这里调用create_boot_cache函数来建立boot_cache。并将它的list成员加入到slab_caches链表中。这是slab_state是PARTIAL。缓存
void __init create_boot_cache(struct kmem_cache *s, const char *name, unsigned int size, slab_flags_t flags, unsigned int useroffset, unsigned int usersize) { int err; s->name = name; s->size = s->object_size = size;//设置object size s->align = calculate_alignment(flags, ARCH_KMALLOC_MINALIGN, size);//计算按多少字节对齐。若是定义了SLAB_HWCACHE_ALIGN,就须要按硬件的cache line对齐。若是obj_size>cache line通常,就是一个obj对齐,不然是多个obj对齐。 s->useroffset = useroffset; s->usersize = usersize; slab_init_memcg_params(s); err = __kmem_cache_create(s, flags); //建立kmem_cache if (err) panic("Creation of kmalloc slab %s size=%u failed. Reason %d\n", name, size, err); s->refcount = -1; /* Exempt from merging for now */ }
首先调用create_boot_cache函数建立第一个cache实例。这里会调用的__kmem_cache_create函数建立一个kmeme_cache。具体函数在后面详细看看。函数
第二个实例是调用create_kmalloc_cache函数spa
struct kmem_cache *__init create_kmalloc_cache(const char *name, unsigned int size, slab_flags_t flags, unsigned int useroffset, unsigned int usersize) { struct kmem_cache *s = kmem_cache_zalloc(kmem_cache, GFP_NOWAIT); if (!s) panic("Out of memory when creating slab %s\n", name); create_boot_cache(s, name, size, flags, useroffset, usersize); list_add(&s->list, &slab_caches); memcg_link_cache(s, NULL); s->refcount = 1; return s; }
由他申请的内存大小能够看出来这个kmem_cache是用来存kmeme_cache_node的。命令行
/* kmalloc_info []用于使slub_debug =,kmalloc-xx选项在引导时起做用。 kmalloc_index()最多支持2 ^ 26 = 64MB,所以该表的最终条目是kmalloc-67108864。 */ 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-1024", 1024}, {"kmalloc-2048", 2048}, {"kmalloc-4096", 4096}, {"kmalloc-8192", 8192}, {"kmalloc-16384", 16384}, {"kmalloc-32768", 32768}, {"kmalloc-65536", 65536}, {"kmalloc-131072", 131072}, {"kmalloc-262144", 262144}, {"kmalloc-524288", 524288}, {"kmalloc-1048576", 1048576}, {"kmalloc-2097152", 2097152}, {"kmalloc-4194304", 4194304}, {"kmalloc-8388608", 8388608}, {"kmalloc-16777216", 16777216}, {"kmalloc-33554432", 33554432}, {"kmalloc-67108864", 67108864} }; static __always_inline unsigned int kmalloc_index(size_t size) { if (!size) return 0; if (size <= KMALLOC_MIN_SIZE) return KMALLOC_SHIFT_LOW; if (KMALLOC_MIN_SIZE <= 32 && size > 64 && size <= 96) return 1; if (KMALLOC_MIN_SIZE <= 64 && size > 128 && size <= 192) return 2; if (size <= 8) return 3; if (size <= 16) return 4; if (size <= 32) return 5; if (size <= 64) return 6; if (size <= 128) return 7; if (size <= 256) return 8; if (size <= 512) return 9; if (size <= 1024) return 10; if (size <= 2 * 1024) return 11; if (size <= 4 * 1024) return 12; if (size <= 8 * 1024) return 13; if (size <= 16 * 1024) return 14; if (size <= 32 * 1024) return 15; if (size <= 64 * 1024) return 16; if (size <= 128 * 1024) return 17; if (size <= 256 * 1024) return 18; if (size <= 512 * 1024) return 19; if (size <= 1024 * 1024) return 20; if (size <= 2 * 1024 * 1024) return 21; if (size <= 4 * 1024 * 1024) return 22; if (size <= 8 * 1024 * 1024) return 23; if (size <= 16 * 1024 * 1024) return 24; if (size <= 32 * 1024 * 1024) return 25; if (size <= 64 * 1024 * 1024) return 26; BUG(); /* 永远不会达到。须要,由于编译器可能会报错 */ return -1; }
kmalloc 如上所示,这里会根据kmeme_cache_node的大小建立一个kmalloc通用缓存缓存 kmalloc_cachesdebug
kmalloc_index能够根据内存的大小来选择kmalloc_caches的下标。指针
kmalloc_caches是一个kmem_cache的指针数组。当kmalloc分配内存时,会根据要分配的内存的大小,从kmalloc_caches数组中选择一个kmem_cache实例来分配对象,由于不一样的kmem_cache维护的是不一样size的object。code
/*用kmalloc分配的内存交换静态kmem_cache_node*/ static void __init init_list(struct kmem_cache *cachep, struct kmem_cache_node *list, int nodeid) { struct kmem_cache_node *ptr; ptr = kmalloc_node(sizeof(struct kmem_cache_node), GFP_NOWAIT, nodeid);//分配kmem_cache_node BUG_ON(!ptr); memcpy(ptr, list, sizeof(struct kmem_cache_node));//将静态kmem_cache_node拷贝到刚申请的kmem_struct_node中 /* * Do not assume that spinlocks can be initialized via memcpy: */ spin_lock_init(&ptr->list_lock); MAKE_ALL_LISTS(cachep, ptr, nodeid); cachep->node[nodeid] = ptr; }
而后调用init_list函数,将静态的的kmem_cache_node复制到kmem_cache中的list指针那里。对象
void __init create_kmalloc_caches(slab_flags_t flags) { int i, type; for (type = KMALLOC_NORMAL; type <= KMALLOC_RECLAIM; type++) { for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) { if (!kmalloc_caches[type][i]) new_kmalloc_cache(i, type, flags); /*大小不是2的幂的高速缓存。必须在两个缓存具备较早的功能以后当即建立它们 */ if (KMALLOC_MIN_SIZE <= 32 && i == 6 && !kmalloc_caches[type][1]) new_kmalloc_cache(1, type, flags); if (KMALLOC_MIN_SIZE <= 64 && i == 7 && !kmalloc_caches[type][2]) new_kmalloc_cache(2, type, flags); } } /* Kmalloc数组现已可用 */ slab_state = UP; } static void __init new_kmalloc_cache(int idx, int type, slab_flags_t flags) { const char *name; if (type == KMALLOC_RECLAIM) { flags |= SLAB_RECLAIM_ACCOUNT; name = kasprintf(GFP_NOWAIT, "kmalloc-rcl-%u", kmalloc_info[idx].size); BUG_ON(!name); } else { name = kmalloc_info[idx].name; } kmalloc_caches[type][idx] = create_kmalloc_cache(name, kmalloc_info[idx].size, flags, 0, kmalloc_info[idx].size); }
最后建立kmalloc的内存。这里下标从KMALLOC_SHIFT_LOW到KMALLOC_SHIFT_HIGH,其实下标是为了cache line对齐