linux内存管理(四)源码解析之伙伴系统alloc_pages/free_pages

本文深入分析Linux内核4.14.159中伙伴系统( buddy system)的内存管理,重点探讨alloc_pages()和free_pages()的实现。alloc_pages()接口通过__alloc_pages_nodemask进行内存分配,涉及ORDER选择、分配标志、快速分配和慢速分配等流程。free_pages()则对应不同ORDER的页释放,包括单页和多页的处理。通过了解这些核心代码,有助于理解Linux内核的内存管理机制。
摘要由CSDN通过智能技术生成

本文kernel代码分析基于以下
1.linux-4.14.159
2.64bit代码处理逻辑
3.UMA架构

上一节伙伴系统详细介绍了伙伴系统的原理以及描述了页申请和释放的过程,另外对伙伴系统中涉及的一些概念也进行了介绍,理解这些后我们来看kernel代码中具体的申请流程和释放流程。

alloc_pages
linux内核伙伴系统中分配物理内存常用的接口是alloc_pages(),其它接口也最终都会走到alloc_pages(),其要求是分配页面的个数是2的整数幂,因此函数无需提供分配的大小,只需指明两个参数
第一个为阶order,第二个为分配掩码gfp_mask。

gfp.h	include\linux
#define alloc_pages(gfp_mask, order) \
		alloc_pages_node(numa_node_id(), gfp_mask, order)

static inline struct page *alloc_pages_node(int nid, gfp_t gfp_mask,
						unsigned int order)
{
   
	if (nid == NUMA_NO_NODE)
		nid = numa_mem_id();
	return __alloc_pages_node(nid, gfp_mask, order);
}
static inline struct page *
__alloc_pages_node(int nid, gfp_t gfp_mask, unsigned int order)
{
   
	VM_BUG_ON(nid < 0 || nid >= MAX_NUMNODES);
	VM_WARN_ON(!node_online(nid));

	return __alloc_pages(gfp_mask, order, nid);
}
static inline struct page *
__alloc_pages(gfp_t gfp_mask, unsigned int order, int preferred_nid)
{
   
	return __alloc_pages_nodemask(gfp_mask, order, preferred_nid, NULL);
}

上面去掉了一些非核心code,从上面的流程我们可以看到,最终会走到__alloc_pages_nodemask ,这个是page分配的心脏,我们重点分析此函数。

page_alloc.c	mm	216036	2019/12/18	4241

/*
 * This is the 'heart' of the zoned buddy allocator.
 */
struct page *
__alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, int preferred_nid,
							nodemask_t *nodemask)
{
   
	struct page *page;
	unsigned int alloc_flags = ALLOC_WMARK_LOW; 
	@1.ALLOC_WMARK_LOW表明free内存页大于WMARK_LOW阈值时才能走页分配逻辑,见下详细解释。
	
	gfp_t alloc_mask; /* The gfp_t that was actually used for allocation */
	struct alloc_context ac = {
    };
	if (unlikely(order >= MAX_ORDER)) {
   
		WARN_ON_ONCE(!(gfp_mask & __GFP_NOWARN));
		return NULL;
	}

	gfp_mask &= gfp_allowed_mask;
	alloc_mask = gfp_mask;
	if (!prepare_alloc_pages(gfp_mask, order, preferred_nid, nodemask, &ac, &alloc_mask, &alloc_flags))
	@2.初始化alloc_context对象ac,保存相关的一些分配参数,见下详细分析
		return NULL;

	finalise_ac(gfp_mask, order, &ac);
    @3. 通过ac的zonelist和high_zoneidx 计算出ac->preferred_zoneref,这个是优先/首先用来分配的zone 
	/* First allocation attempt */
	page = get_page_from_freelist(alloc_mask, order, alloc_flags, &ac);
	@4. 这个接口可认为是快速分配逻辑,先通过它进行快速分配,万一这里失败则会到慢速分配流程做进一步处理,见下详细分析
	if (likely(page))
	@5. 分配成功跳出,否则调用下面慢速分配继续处理
		goto out;
	
	alloc_mask = current_gfp_context(gfp_mask);
	ac.spread_dirty_pages = false;

	if (unlikely(ac.nodemask != nodemask))
		ac.nodemask = nodemask;

	page = __alloc_pages_slowpath(alloc_mask, order, &ac);
	@6. 走到这里说明上面快速分配失败,需要通过__alloc_pages_slowpath进行慢速分配,见下详细分析

out:
	if (memcg_kmem_enabled() && (gfp_mask & __GFP_ACCOUNT) && page &&
	    unlikely(memcg_kmem_charge(page, gfp_mask, order) != 0)) {
   
		__free_pages(page, order);
		page = NULL;
	}

	trace_mm_page_alloc(page, order, alloc_mask, ac.migratetype);

	return page;
}

@1. 这个是分配标志,如下几个是常用的,如ALLOC_NO_WATERMARKS:表示分配页不考虑水位标志;ALLOC_HARDER:条件放宽,最大可能的申请内存,如当前阶失败可以向更高阶分配内存。

internal.h	mm	17253	2019/12/18	100
/* The ALLOC_WMARK bits are used as an index to zone->watermark */
#define ALLOC_WMARK_MIN		WMARK_MIN
#define ALLOC_WMARK_LOW		WMARK_LOW
#define ALLOC_WMARK_HIGH	WMARK_HIGH
#define ALLOC_NO_WATERMARKS	0x04 /* don't check watermarks at all */

#define ALLOC_HARDER		0x10 /* try to alloc harder */
#define ALLOC_HIGH		0x20 /* __GFP_HIGH set */

@2. 下面主要对ac结构体进行初始化赋值

static inline bool prepare_alloc_pages(gfp_t gfp_mask, unsigned int order,
		int preferred_nid, nodemask_t *nodemask,
		struct alloc_context *ac, gfp_t *alloc_mask,
		unsigned int *alloc_flags)
{
   
	ac->high_zoneidx = gfp_zone(gfp_mask);
	@2.1 获取掩码中指定的zone,表示可供分配的那些zone中最高的那个zone的index 
	ac->zonelist = node_zonelist(preferred_nid, gfp_mask);
	@2.2 可供内存页分配的zone list
	ac->nodemask = nodemask;
	ac->migratetype = gfpflags_to_migratetype(gfp_mask);
	@2.3 页的迁移类型
	if (cpusets_enabled()) {
   
	@2.4 非NUMA架构暂不考虑cpuset功能
		*alloc_mask |= __GFP_HARDWALL;
		if (!ac->nodemask)
			ac->nodemask = &cpuset_current_mems_allowed;
		else
			*alloc_flags |= ALLOC_CPUSET;
	}

	fs_reclaim_acquire(gfp_mask);
	fs_reclaim_release(gfp_mask);    
	might_sleep_if(gfp_mask & __GFP_DIRECT_RECLAIM);
    @2.5 上面debug相关
	if (should_fail_alloc_page(gfp_mask, order))
		return false;

	if (IS_ENABLED(CONFIG_CMA) && ac->migratetype == MIGRATE_MOVABLE)
		*alloc_flags |= ALLOC_CMA;

	return true;
}

@4. 我们详细看下get_page_from_freelist这个快速分配如何进行

static struct page *
get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
						const struct alloc_context *ac)
{
   
	struct zoneref *z = ac->preferred_zoneref;
	struct zone *zone;
	struct pglist_data *last_pgdat_dirty_limit = NULL
	/*
	 * Scan zonelist, looking for a zone with enough free.
	 * See also __cpuset_node_allowed() comment in kernel/cpuset.c.
	 */
	for_next_zone_zonelist_nodemask(zone, z, ac->zonelist, ac->high_zoneidx,
								ac->nodemask) {
   
	@4.1 遍历zonelist,从ac->preferred_zoneref这个zone开始,结束判断条件是小于等于ac->high_zoneidx。
	例:high_zoneidx=1,就是类似NORMAL(idx=1
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值