【內核源碼學習筆記】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對齊

相關文章
相關標籤/搜索