所属专栏: 深入浅出内存管理
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/rikeyone/article/details/85766256
首先我们来看下kmalloc的实现,本文基于kernel 4.0版本:
static __always_inline void *kmalloc(size_t size, gfp_t flags)
{
if (__builtin_constant_p(size)) { -----------(1)
if (size > KMALLOC_MAX_CACHE_SIZE)
return kmalloc_large(size, flags); --------------(2)
#ifndef CONFIG_SLOB
if (!(flags & GFP_DMA)) {
int index = kmalloc_index(size);
if (!index)
return ZERO_SIZE_PTR;
return kmem_cache_alloc_trace(kmalloc_caches[index], -----------------(3)
flags, size);
}
#endif
}
return __kmalloc(size, flags);------------------(4)
}
(1) __builtin_constant_p表示传入的是否为一个实数,gcc编译器会做这个判断,如果是一个确定的实数而非变量,那么它返回true,主要用于编译优化的处理。
(2) 如果是实数,那么会判断size是否大于KMALLOC_MAX_CACHE_SIZE,此值表示的是系统创建的slab cache的最大值,系统为kmalloc预先创建了很多大小不同的kmem cache,用于内存分配。这里的含义就是如果内存申请超过此值,那么直接使用 kmalloc_large进行大内存分配,实际上最终会调用页分配器去分配内存,而不是使用slab分配器。
(3) kmalloc_large分配大内存,后面会讲到,实际上也就是调用页分配器去分配内存。
(4) 最后如果是一个变量,那么会调用__kmalloc来进行分配。
和此函数相关的宏定义有如下几个:
#define MAX_ORDER 11
#define PAGE_SHIFT 12
#define KMALLOC_SHIFT_HIGH ((MAX_ORDER + PAGE_SHIFT - 1) <= 25 ? \
(MAX_ORDER + PAGE_SHIFT - 1) : 25)
#define KMALLOC_SHIFT_MAX KMALLOC_SHIFT_HIGH
/* Maximum allocatable size */
#define KMALLOC_MAX_SIZE (1UL << KMALLOC_SHIFT_MAX)
/* Maximum size for which we actually use a slab cache */
#define KMALLOC_MAX_CACHE_SIZE (1UL << KMALLOC_SHIFT_HIGH)
/* Maximum order allocatable via the slab allocagtor */
#define KMALLOC_MAX_ORDER (KMALLOC_SHIFT_MAX - PAGE_SHIFT)
这里可能每个平台定义不同,以我的arm32为例,经过换算可知,KMALLOC_MAX_SIZE是4M。
KMALLOC_MAX_SIZE = (1<<22) = 4M
KMALLOC_MAX_CACHE_SIZE = (1<<22) = 4M
KMALLOC_SHIFT_HIGH = 22
KMALLOC_SHIFT_MAX = 22
__kmalloc路径
static __always_inline void *__do_kmalloc(size_t size, gfp_t flags,
unsigned long caller)
{
struct kmem_cache *cachep;
void *ret;
cachep = kmalloc_slab(size, flags);
if (unlikely(ZERO_OR_NULL_PTR(cachep)))
return cachep;
ret = slab_alloc(cachep, flags, caller);
trace_kmalloc(caller, ret,
size, cachep->size, flags);
return ret;
}
void *__kmalloc(size_t size, gfp_t flags)
{
return __do_kmalloc(size, flags, _RET_IP_);
}
当代码跑到__kmalloc之后,我们继续跟进,发现会先查找kmalloc_slab,然后在对应的slab kmem cache中去申请内存来使用,采用slab_alloc来申请内存。那么内存大小的限制在如下代码中得以体现:
struct kmem_cache *kmalloc_slab(size_t size, gfp_t flags)
{
int index;
if (unlikely(size > KMALLOC_MAX_SIZE)) {
WARN_ON_ONCE(!(flags & __GFP_NOWARN));
return NULL;
}
if (size <= 192) {
if (!size)
return ZERO_SIZE_PTR;
index = size_index[size_index_elem(size)];
} else
index = fls(size - 1);
#ifdef CONFIG_ZONE_DMA
if (unlikely((flags & GFP_DMA)))
return kmalloc_dma_caches[index];
#endif
return kmalloc_caches[index];
}
在这里判断了size大小是否超过KMALLOC_MAX_SIZE,也就是前面定义的4M,如果超过了就返回NULL,最终kmalloc申请失败。
kmalloc_large
想象一下,如果我们调用了一次内存分配函数,代码跑到了kmalloc_large,并且此size超过了伙伴系统能够支持的最大申请大小,比如order>11个page大小的内存,那么系统会在哪里判断返回呢?带着这个问题,我们看一下它的实现过程:
static __always_inline void *kmalloc_large(size_t size, gfp_t flags)
{
unsigned int order = get_order(size);
return kmalloc_order_trace(size, flags, order);
}
get_order返回值如下:
0 -> 2^0 * PAGE_SIZE and below
1 -> 2^1 * PAGE_SIZE to 2^0 * PAGE_SIZE + 1
2 -> 2^2 * PAGE_SIZE to 2^1 * PAGE_SIZE + 1
3 -> 2^3 * PAGE_SIZE to 2^2 * PAGE_SIZE + 1
4 -> 2^4 * PAGE_SIZE to 2^3 * PAGE_SIZE + 1
紧接着,函数调用到kmalloc_order:
void *kmalloc_order(size_t size, gfp_t flags, unsigned int order)
{
void *ret;
struct page *page;
flags |= __GFP_COMP;
page = alloc_kmem_pages(flags, order);
ret = page ? page_address(page) : NULL;
kmemleak_alloc(ret, size, 1, flags);
kasan_kmalloc_large(ret, size);
return ret;
}
进一步调用到alloc_kmem_pages:
struct page *alloc_kmem_pages(gfp_t gfp_mask, unsigned int order)
{
struct page *page;
struct mem_cgroup *memcg = NULL;
if (!memcg_kmem_newpage_charge(gfp_mask, &memcg, order))
return NULL;
page = alloc_pages(gfp_mask, order);
memcg_kmem_commit_charge(page, memcg, order);
return page;
}
最终调用到了alloc_pages,看过我前面文章的应该很熟悉了,这个就是页分配器的接口了,最终是会利用伙伴系统算法进行页的分配。看下伙伴系统核心:
static inline
struct page *__rmqueue_smallest(struct zone *zone, unsigned int order,
int migratetype)
{
unsigned int current_order;
struct free_area *area;
struct page *page;
/* Find a page of the appropriate size in the preferred list */
for (current_order = order; current_order < MAX_ORDER; ++current_order) {
area = &(zone->free_area[current_order]);
if (list_empty(&area->free_list[migratetype]))
continue;
page = list_entry(area->free_list[migratetype].next,
struct page, lru);
list_del(&page->lru);
rmv_page_order(page);
area->nr_free--;
expand(zone, page, order, current_order, area, migratetype);
set_freepage_migratetype(page, migratetype);
return page;
}
return NULL;
}
终于在这里有个current_order < MAX_ORDER的限制,这里也就是限制伙伴系统能够分配的内存大小最大不超过2^(MAX_ORDER-1)个page,经过计算可知:
2 ^ 10 * 4K = 4M
因此在我的平台上最大使用kmalloc申请不能超过4M大小。