文章目錄
- kmalloc的整體實現
- 獲取高速緩存
- 高速緩存
- 獲取index
- 總結
https://blog.csdn.net/qq_41683305/article/details/124554490,在這篇文章中,我們介紹了伙伴算法、slab機制和常見的內存管理函數,接下來,我們看看kmalloc內核函數的具體實現。
kmalloc() 分配連續的物理地址,用于小內存分配。get_free_page() 分配連續的物理地址,用于整頁分配。在slab機制下(還有slob、slub),kmalloc() 函數是基于 slab 實現的。slab 是為分配小內存提供的一種高效機制。但 slab 這種分配機制又不是獨立的,它本身也是在頁分配器的基礎上來劃分更細粒度的內存供調用者使用。也就是說系統先用頁分配器分配以頁為最小單位的連續物理地址,然后 kmalloc() 再在這上面根據調用者的需要進行切分。
我們可以調用kmalloc獲取slab中未分配的對象,比如kmalloc(30,GFP_KERNAL),系統會從slab中分配一個32字節的對象。
kmalloc的整體實現
kmalloc的實現如下:
static __always_inline void *kmalloc(size_t size, gfp_t flags)
{if (__builtin_constant_p(size)) {if (size > KMALLOC_MAX_CACHE_SIZE)return kmalloc_large(size, flags);
#ifndef CONFIG_SLOBif (!(flags & GFP_DMA)) {int index = kmalloc_index(size);if (!index)return ZERO_SIZE_PTR;return kmem_cache_alloc_trace(kmalloc_caches[index],flags, size);}
#endif}return __kmalloc(size, flags);
}
- __builtin_constant_p表示傳入的是否為一個實數,gcc編譯器會做這個判斷,如果是一個確定的實數而非變量,那么它返回true,主要用于編譯優化的處理。
- 如果是實數,那么會判斷size是否大于KMALLOC_MAX_CACHE_SIZE,此值表示的是系統創建的slab cache(slab創建的對象)的最大值,系統為kmalloc預先創建了很多大小不同的kmem cache,用于內存分配。這里的含義就是如果內存申請超過此值,那么直接使用 kmalloc_large進行大內存分配,實際上最終會調用頁分配器去分配內存,而不是使用slab分配器。
- 如果沒有大于KMALLOC_MAX_CACHE_SIZE,會調用__kmalloc來進行分配內存。
我們看的是slab下kmalloc的實現,所以。我們看mm/slab.c的__kmalloc,slob.c,slub.c也有該函數的實現。
void *__kmalloc(size_t size, gfp_t flags)
{return __do_kmalloc(size, flags, _RET_IP_);
}/*** __do_kmalloc - allocate memory* @size: how many bytes of memory are required.* @flags: the type of memory to allocate (see kmalloc).* @caller: function caller for debug tracking of the caller*/
static __always_inline void *__do_kmalloc(size_t size, gfp_t flags,unsigned long caller)
{struct kmem_cache *cachep;void *ret;if (unlikely(size > KMALLOC_MAX_CACHE_SIZE))return NULL;cachep = kmalloc_slab(size, flags);if (unlikely(ZERO_OR_NULL_PTR(cachep)))return cachep;ret = slab_alloc(cachep, flags, caller);kasan_kmalloc(cachep, ret, size, flags);trace_kmalloc(caller, ret,size, cachep->size, flags);return ret;
}
在__do_kmalloc會先調用kmalloc_slab找到一個高速緩存cachep,然后調用slab_alloc從高速緩存cachep分配一個對象,最后返回。kasan_kmalloc和trace_kmalloc是內存調試用的,我們可以忽略。
獲取高速緩存
接下來我們分析怎么得到一個高速緩存的,kmalloc_slab的實現在mm/slab_common.c中。
/** Find the kmem_cache structure that serves a given size of* allocation*/
struct kmem_cache *kmalloc_slab(size_t size, gfp_t flags)
{int index;if (size <= 192) {if (!size)return ZERO_SIZE_PTR;index = size_index[size_index_elem(size)];} else {if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) {WARN_ON(1);return NULL;}index = fls(size - 1);}#ifdef CONFIG_ZONE_DMAif (unlikely((flags & GFP_DMA)))return kmalloc_dma_caches[index];#endifreturn kmalloc_caches[index];
}
該函數會首先獲得一個索引index,然后返回kmalloc_caches[index],說明kmalloc_caches[index]就是我們需要的高速緩存,kmalloc_caches是系統已經分配好的高速緩存,和slab機制中的高速緩存對應。
高速緩存
struct kmem_cache *kmalloc_caches[KMALLOC_SHIFT_HIGH + 1];
KMALLOC_SHIFT_HIGH 的值在slab、slob、slub下不同,我們看slab下所定義的值:
#define KMALLOC_SHIFT_HIGH ((MAX_ORDER + PAGE_SHIFT - 1) <= 25 ? \(MAX_ORDER + PAGE_SHIFT - 1) : 25)/* Free memory management - zoned buddy allocator. */
#ifndef CONFIG_FORCE_MAX_ZONEORDER
#define MAX_ORDER 11
#else
#define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER
#endif/* PAGE_SHIFT determines the page size */
#define PAGE_SHIFT 12
如果沒有定義CONFIG_FORCE_MAX_ZONEORDER,MAX_ORDER 是11,代表的是伙伴算法的11個塊鏈表,MAX_ORDER 決定伙伴算法中的塊鏈表數,PAGE_SHIFT與具體架構有關,我們看的是arm下的大小,PAGE_SHIFT 代表的是頁大小,12位,共4KB,所以在arm下一個頁大小為4KB。
在這種情況下,KMALLOC_SHIFT_HIGH 的大小是22。
kmalloc_caches是內核自己生成的,通過調用create_kmalloc_caches函數
/** Create the kmalloc array. Some of the regular kmalloc arrays* may already have been created because they were needed to* enable allocations for slab creation.*/
void __init create_kmalloc_caches(unsigned long flags)
{int i;for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) {if (!kmalloc_caches[i])new_kmalloc_cache(i, flags);/** Caches that are not of the two-to-the-power-of size.* These have to be created immediately after the* earlier power of two caches*/if (KMALLOC_MIN_SIZE <= 32 && !kmalloc_caches[1] && i == 6)new_kmalloc_cache(1, flags);if (KMALLOC_MIN_SIZE <= 64 && !kmalloc_caches[2] && i == 7)new_kmalloc_cache(2, flags);}/* Kmalloc array is now usable */slab_state = UP;#ifdef CONFIG_ZONE_DMAfor (i = 0; i <= KMALLOC_SHIFT_HIGH; i++) {struct kmem_cache *s = kmalloc_caches[i];if (s) {int size = kmalloc_size(i);char *n = kasprintf(GFP_NOWAIT,"dma-kmalloc-%d", size);BUG_ON(!n);kmalloc_dma_caches[i] = create_kmalloc_cache(n,size, SLAB_CACHE_DMA | flags);}}
#endif
}
KMALLOC_SHIFT_LOW在slab中定義的是5
create_kmalloc_caches調用new_kmalloc_cache,new_kmalloc_cache調用create_kmalloc_cache最終創建高速緩存
static void __init new_kmalloc_cache(int idx, unsigned long flags)
{kmalloc_caches[idx] = create_kmalloc_cache(kmalloc_info[idx].name,kmalloc_info[idx].size, flags);
}struct kmem_cache *__init create_kmalloc_cache(const char *name, size_t size,unsigned long flags)
{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);list_add(&s->list, &slab_caches);s->refcount = 1;return s;
}
kmalloc_info有25個,定義了25個高速緩存的名字和大小
/** kmalloc_info[] is to make slub_debug=,kmalloc-xx option work at boot time.* kmalloc_index() supports up to 2^26=64MB, so the final entry of the table is* kmalloc-67108864.*/
static struct {const char *name;unsigned long size;
} const 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}
};
獲取index
如果size小于等于192,index = size_index[size_index_elem(size)];
/* 8字節對齊 */
static inline int size_index_elem(size_t bytes)
{return (bytes - 1) / 8;
}/** Conversion table for small slabs sizes / 8 to the index in the* kmalloc array. This is necessary for slabs < 192 since we have non power* of two cache sizes there. The size of larger slabs can be determined using* fls.*/
static s8 size_index[24] = {3, /* 8 */4, /* 16 */5, /* 24 */5, /* 32 */6, /* 40 */6, /* 48 */6, /* 56 */6, /* 64 */1, /* 72 */1, /* 80 */1, /* 88 */1, /* 96 */7, /* 104 */7, /* 112 */7, /* 120 */7, /* 128 */2, /* 136 */2, /* 144 */2, /* 152 */2, /* 160 */2, /* 168 */2, /* 176 */2, /* 184 */2 /* 192 */
};
如果size大于192,index = fls(size - 1);
fls函數返回的是size-1的高位0的個數,比如0x800000,返回的是0,如果是0x00000000,返回的就是32。
總結
kmalloc獲取內存步驟如下:
- 判斷分配的內存是不是大于slab所能分配對象的最大值,如果大于則調用kmalloc_large,使用頁分配器分配,小于等于使用slab分配器分配內存,調用__kmalloc函數。
- __kmalloc調用__do_kmalloc來實現
- 在__do_kmalloc會先調用kmalloc_slab分配一個高速緩存,然后調用slab_alloc從高速緩存中分配一個對象,最后將該對象的首地址返回就是kmalloc獲取的內存