linux内存管理(十六)-kmalloc函数解析

1. kmalloc

kmalloc函数解析:

static __always_inline void *kmalloc(size_t size, gfp_t flags)
{
	if (__builtin_constant_p(size)) {
#ifndef CONFIG_SLOB
		unsigned int index;
#endif
		if (size > KMALLOC_MAX_CACHE_SIZE)
			return kmalloc_large(size, flags);//大于8k,从伙伴系统中分配内存
#ifndef CONFIG_SLOB
		index = kmalloc_index(size);//计算出需要从slab的那个层分配内存

		if (!index)
			return ZERO_SIZE_PTR;

		return kmem_cache_alloc_trace(		//小于8k,从slab管理器中分配内存
				kmalloc_caches[kmalloc_type(flags)][index],
				flags, size);
#endif
	}
	return __kmalloc(size, flags);
}

kmalloc首先判断需要申请的内存大小,如果比较大,则调用kmalloc_large从伙伴系统中分配内存;如果比较小,则从slab管理器中分配内存。分界线是KMALLOC_MAX_CACHE_SIZE,根据我这边的实际情况,我们使用的是SLUB,也就是CONFIG_SLUB=y,所以定义如下:

#define KMALLOC_MAX_CACHE_SIZE	(1UL << KMALLOC_SHIFT_HIGH)
#define KMALLOC_SHIFT_HIGH	(PAGE_SHIFT + 1)

也就是,分界线是8k,大于8k会调用kmalloc_large从伙伴系统中分配内存;小于8k则会调用kmem_cache_alloc_trace从slab管理器中分配内存。

1.1 kmalloc_large(size, flags)

static __always_inline void *kmalloc_large(size_t size, gfp_t flags)
{
	unsigned int order = get_order(size);//计算出从伙伴系统中申请内存的order
	return kmalloc_order_trace(size, flags, order);//真正的分配函数
}

我们可以看到,kmalloc从伙伴系统分配页面首先通过get_order根据size计算出需要分配的order,然后调用kmalloc_order_trace进行页面的分配。着重看kmalloc_order_trace:

void *kmalloc_order_trace(size_t size, gfp_t flags, unsigned int order)
{
	void *ret = kmalloc_order(size, flags, order);//从伙伴系统分配页面内存的核心函数
	trace_kmalloc(_RET_IP_, ret, size, PAGE_SIZE << order, flags);//tracer跟踪kmalloc,debug用的
	return ret;
}

kmalloc_order_trace函数的作用是调用kmalloc_order进行真正的页面分配,然后在trace_kmalloc函数中使用tracer动态跟踪kmalloc。trace_kmalloc函数我们不用关心,我们看kmalloc_order:

void *kmalloc_order(size_t size, gfp_t flags, unsigned int order)
{
	void *ret = NULL;
	struct page *page;

	if (unlikely(flags & GFP_SLAB_BUG_MASK))
		flags = kmalloc_fix_flags(flags);

	flags |= __GFP_COMP;//复合页,可以知道在kfree中正确释放页面的分配顺序
	page = alloc_pages(flags, order);//从伙伴系统中分配页面的函数
	if (likely(page)) {//分支预测,分配成功
		ret = page_address(page);//获取页面的虚拟地址
		mod_lruvec_page_state(page, NR_SLAB_UNRECLAIMABLE_B,
				      PAGE_SIZE << order);
	}
	ret = kasan_kmalloc_large(ret, size, flags);//KASAN工具检测内存问题
	/* As ret might get tagged, call kmemleak hook after KASAN. */
	kmemleak_alloc(ret, size, 1, flags);//内存检测用到,注册内存对象
	return ret;
}

kmalloc_order就是真正进行页面分配的函数,首先flags加上__GFP_COMP标志,这样子就是使用了复合页,然后通过alloc_pages从伙伴系统中分配页面,如果分配成功,再调用page_address函数,把page结构体转化为虚拟地址,最后返回虚拟地址。alloc_pages函数在前面的伙伴系统中已经讲过了,这里不再多说,把page结构体转化为虚拟地址的page_address:

static __always_inline void *lowmem_page_address(const struct page *page)
{
	return page_to_virt(page);
}

#define page_to_virt(x)	({						\
	__typeof__(x) __page = x;					\
	u64 __idx = ((u64)__page - VMEMMAP_START) / sizeof(struct page);\
	u64 __addr = PAGE_OFFSET + (__idx * PAGE_SIZE);			\
	(void *)__tag_set((const void *)__addr, page_kasan_tag(__page));\
})

static inline const void *__tag_set(const void *addr, u8 tag)
{
	u64 __addr = (u64)addr & ~__tag_shifted(0xff);
	return (const void *)(__addr | __tag_shifted(tag));
}

#define __tag_shifted(tag)	0UL
#define VMEMMAP_START		(-VMEMMAP_SIZE - SZ_2M)

根据这些宏,我们可以把lowmem_page_address函数看做:

static __always_inline void *lowmem_page_address(const struct page *page)
{				
	u64 __idx = ((u64)page - VMEMMAP_START) / sizeof(struct page);
	u64 __addr = PAGE_OFFSET + (__idx * PAGE_SIZE);			
	return __addr ;
}

看到这里我有点懵逼,需要捋一捋。

1.2 kmem_cache_alloc_trace(kmalloc_caches[kmalloc_type(flags)][index],flags, size)

static __always_inline void *kmem_cache_alloc_trace(struct kmem_cache *s,
		gfp_t flags, size_t size)
{
	void *ret = kmem_cache_alloc(s, flags);

	ret = kasan_kmalloc(s, ret, size, flags);
	return ret;
}

kmem_cache_alloc_trace主要是调用kmem_cache_alloc从slab分配内存块,这个函数我们在之前的slab管理器中讲过,这里就不在多说了。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小坚学Linux

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值