kmalloc最大能申请多少内存?

1. 概述

本文主要分析kmalloc接口申请内存的大小情况,用于记录kmalloc分配内存的过程。

内核版本:Linux 4.9

2.分析记录

针对kmalloc最大能申请多少内存,网上众说纷纭,意见各不相同,因此最终决定自己针对源码分析,记录如下:

首先看kmalloc()函数实现,在include/linux/slab.h中,代码如下:

#ifdef CONFIG_SLAB
/*
 * The largest kmalloc size supported by the SLAB allocators is
 * 32 megabyte (2^25) or the maximum allocatable page order if that is
 * less than 32 MB.
 *
 * WARNING: Its not easy to increase this value since the allocators have
 * to do various tricks to work around compiler limitations in order to
 * ensure proper constant folding.
 */
#define KMALLOC_SHIFT_HIGH	((MAX_ORDER + PAGE_SHIFT - 1) <= 25 ? \
				(MAX_ORDER + PAGE_SHIFT - 1) : 25)
#define KMALLOC_SHIFT_MAX	KMALLOC_SHIFT_HIGH
#ifndef KMALLOC_SHIFT_LOW
#define KMALLOC_SHIFT_LOW	5
#endif
#endif

#ifdef CONFIG_SLUB
/*
 * SLUB directly allocates requests fitting in to an order-1 page
 * (PAGE_SIZE*2).  Larger requests are passed to the page allocator.
 */
#define KMALLOC_SHIFT_HIGH	(PAGE_SHIFT + 1)
#define KMALLOC_SHIFT_MAX	(MAX_ORDER + PAGE_SHIFT - 1)
#ifndef KMALLOC_SHIFT_LOW
#define KMALLOC_SHIFT_LOW	3
#endif
#endif

#ifdef CONFIG_SLOB
/*
 * SLOB passes all requests larger than one page to the page allocator.
 * No kmalloc array is necessary since objects of different sizes can
 * be allocated from the same page.
 */
#define KMALLOC_SHIFT_HIGH	PAGE_SHIFT
#define KMALLOC_SHIFT_MAX	(MAX_ORDER + PAGE_SHIFT - 1)
#ifndef KMALLOC_SHIFT_LOW
#define KMALLOC_SHIFT_LOW	3
#endif
#endif

/* 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)

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_SLOB
		if (!(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()是编译器内函数,用于判断传入的参数是否为常量,因此动态分配内存一般不会进入该if分支,重点分析__kmalloc()实现即可。

__kmalloc()实现分三种情况,一种是slab,一种是slub,最后一种是slob,其实现分别如下:

2.1 slab的kmalloc()


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;
}

void *__kmalloc(size_t size, gfp_t flags)
{
	return __do_kmalloc(size, flags, _RET_IP_);
}
EXPORT_SYMBOL(__kmalloc);

当Linux系统配置的内存管理器为slab时,如果分配的内存大于KMALLOC_MAX_CACHE_SIZE,就直接返回NULL,因此支持slab分配器的系统,kmalloc()分配的内存不能超过KMALLOC_MAX_CACHE_SIZE,该宏的计算见include/linux/slab.h。

当MAX_ORDER=11,PAGE_SHIFT=12(4kB)时,KMALLOC_MAX_CACHE_SIZE算出来是4MB,说明支持slab的系统,通过kmalloc()最大申请的内存不能超过4M。

但该值是跟随CONFIG_FORCE_MAX_ZONEORDER配置项和PAGE_SHIFT这两个宏决定。因此具体还需要看系统中这两个宏的值。

2.2 slub的kmalloc()

//mm/slub.c
void *__kmalloc(size_t size, gfp_t flags)
{
	struct kmem_cache *s;
	void *ret;

	if (unlikely(size > KMALLOC_MAX_CACHE_SIZE))
		return kmalloc_large(size, flags);

	s = kmalloc_slab(size, flags);

	if (unlikely(ZERO_OR_NULL_PTR(s)))
		return s;

	ret = slab_alloc(s, flags, _RET_IP_);

	trace_kmalloc(_RET_IP_, ret, size, s->size, flags);

	kasan_kmalloc(s, ret, size, flags);

	return ret;
}
EXPORT_SYMBOL(__kmalloc);
//mm/slab_common.c
void *kmalloc_order(size_t size, gfp_t flags, unsigned int order)
{
	void *ret;
	struct page *page;

	flags |= __GFP_COMP;
	page = alloc_pages(flags, order);
	ret = page ? page_address(page) : NULL;
	kmemleak_alloc(ret, size, 1, flags);
	kasan_kmalloc_large(ret, size, flags);
	return ret;
}
EXPORT_SYMBOL(kmalloc_order);

当Linux系统配置的内存管理器为slub时,如果分配的内存大于KMALLOC_MAX_CACHE_SIZE,就通过kmalloc_large()接口分配内存,最终会调用到kmalloc_order()函数,该函数直接从buddy子系统分配pages。因此只要系统上内存足够,就可以分配出足够大的内存,只是该内存不是属于slub管理器管理的对象而已。

系统支持slub内存管理器,KMALLOC_MAX_CACHE_SIZE宏的值只和PAGE_SHIFT有关,当PAGE_SHIFT=12时,KMALLOC_MAX_CACHE_SIZE算出来是8kB

2.3 slob的kmalloc()

//mm/slob.c

static __always_inline void *
__do_kmalloc_node(size_t size, gfp_t gfp, int node, unsigned long caller)
{
	unsigned int *m;
	int align = max_t(size_t, ARCH_KMALLOC_MINALIGN, ARCH_SLAB_MINALIGN);
	void *ret;

	gfp &= gfp_allowed_mask;

	lockdep_trace_alloc(gfp);

	if (size < PAGE_SIZE - align) {
		if (!size)
			return ZERO_SIZE_PTR;

		m = slob_alloc(size + align, gfp, align, node);

		if (!m)
			return NULL;
		*m = size;
		ret = (void *)m + align;

		trace_kmalloc_node(caller, ret,
				   size, size + align, gfp, node);
	} else {
		unsigned int order = get_order(size);

		if (likely(order))
			gfp |= __GFP_COMP;
		ret = slob_new_pages(gfp, order, node);

		trace_kmalloc_node(caller, ret,
				   size, PAGE_SIZE << order, gfp, node);
	}

	kmemleak_alloc(ret, size, 1, gfp);
	return ret;
}

void *__kmalloc(size_t size, gfp_t gfp)
{
	return __do_kmalloc_node(size, gfp, NUMA_NO_NODE, _RET_IP_);
}
EXPORT_SYMBOL(__kmalloc);

当Linux系统配置的内存管理器为slob时,如果分配的内存大于PAGE_SIZE,就会从buddy子系统分配pages。因此只要系统上内存足够,就可以分配出足够大的内存。

3.结论

综上所述,kmalloc()能分配多大内存,是依赖系统的配置决定。

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页