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對齊