【内核源码学习笔记】slab分配器(2)kmem_cache初始化

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对齐

相关文章
相关标签/搜索