内存快速分配和慢速分配

本文详细介绍了Linux内核中内存分配的快慢路径,包括__alloc_pages_nodemask()函数的核心作用,快路径的get_page_from_freelist和rmqueue操作,以及慢路径中的内存回收和压缩过程。通过对内存分配机制的理解,有助于优化系统的内存管理。
摘要由CSDN通过智能技术生成

内存快速分配和慢速分配

内存页面的分配最终都交由伙伴系统的页面分配器。页面分配的函数在内核有各种各样的实现,但最终都会调用一个共同的接口::__alloc_pages_nodemask()

常见的页面分配的API

__alloc_pages_node  /*返回struct page的指针*/
    __alloc_pages
    	__alloc_pages_nodemask

alloc_pages         /*返回struct page的指针*/
    alloc_pages_current
    	__alloc_pages_nodemask
        
__get_free_pages    /*返回页面的虚拟地址*/
    __get_free_pages
    	alloc_pages
            alloc_pages_current
            	__alloc_pages_nodemask

他们最终都调用了__alloc_pages_nodemask。

伙伴系统的心脏

__alloc_pages_nodemask()是伙伴系统的心脏

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;
	gfp_t alloc_mask; /* The gfp_t that was actually used for allocation */
	struct alloc_context ac = {
    };

	/*
	 * There are several places where we assume that the order value is sane
	 * so bail out early if the request is out of bound.
	 */
	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))
		return NULL;

	finalise_ac(gfp_mask, &ac);

	/*
	 * Forbid the first pass from falling back to types that fragment
	 * memory until all local zones are considered.
	 */
	alloc_flags |= alloc_flags_nofragment(ac.preferred_zoneref->zone, gfp_mask);	

	/* First allocation attempt */
	page = get_page_from_freelist(alloc_mask, order, alloc_flags, &ac);
	if (likely(page))
		goto out;

	/*
	 * Apply scoped allocation constraints. This is mainly about GFP_NOFS
	 * resp. GFP_NOIO which has to be inherited for all allocation requests
	 * from a particular context which has been marked by
	 * memalloc_no{fs,io}_{save,restore}.
	 */
	alloc_mask = current_gfp_context(gfp_mask);
	ac.spread_dirty_pages = false;

	/*
	 * Restore the original nodemask if it was potentially replaced with
	 * &cpuset_current_mems_allowed to optimize the fast-path attempt.
	 */
	if (unlikely(ac.nodemask != nodemask))
		ac.nodemask = nodemask;

	page = __alloc_pages_slowpath(alloc_mask, order, &ac);

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;
}
EXPORT_SYMBOL(__alloc_pages_nodemask);

通过上述源码其实可以总结出__alloc_pages_nodemask它的核心其实是做了3件事:

prepare_alloc_context   //1.准备参数
alloc_flags_nofragment  //2.根据区域和gfp掩码请求添加分配标志
get_page_from_freelist  //3.快路径尝试分配内存 
__alloc_pages_slowpath  //4.慢路径尝试分配内存

prepare_alloc_context

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);
	ac->zonelist = node_zonelist(preferred_nid, gfp_mask);
	ac->nodemask = nodemask;
	ac->migratetype = gfpflags_to_migratetype(gfp_mask);

	if (cpusets_enabled()) {
		*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);

	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;

prepare_alloc_context它主要是做了如下的事情:

1.填充alloc_context结构体

2.对gfp掩码做处理存放在alloc_mask中

3.填充alloc_flags字段

他做完预备工作之后,执行finalise_ac获得可分配的内存域zone。

alloc_flags_nofragment

static inline unsigned int
alloc_flags_nofragment(struct zone *zone, gfp_t gfp_mask)
{
   
	unsigned int alloc_flags = 0;

	if (gfp_mask & __GFP_KSWAPD_RECLAIM)
		alloc_flags |= ALLOC_KSWAPD;

#ifdef CONFIG_ZONE_DMA32
	if (!zone)
		return alloc_flags;

	if (zone_idx(zone) != ZONE_NORMAL)
		return alloc_flags;

	/*
	 * If ZONE_DMA32 exi
Linux内存慢速申请策略是指在系统内存紧张的情况下,为进程分配内存的一种策略。当系统内存不足时,操作系统需要采取措施来调度和管理可用内存,以实现公平的内存分配Linux内核使用了基于页面的内存管理机制。当一个进程请求分配内存时,操作系统会尝试从空闲内存池中分配一页或多页的内存给该进程。当没有足够连续的空闲内存页可供分配时,操作系统将触发一种称为"慢速申请策略"的机制,即系统将尝试通过动态页面调度来释放足够的可用内存Linux内核会根据内存需求情况进行页面换入和换出操作,从而在进程之间动态调整可用内存。具体来说,当系统内存紧张时,内核会通过将一部分内存页从磁盘交换区(swap space)中换入到内存中来释放可用内存。同时,对于不活跃的进程,内核还可以将其页面换出到磁盘交换区,从而释放更多的可用内存。 此外,Linux内核还使用了页回写、页面按需分配和页面归还等技术来调整内存的使用情况。页回写指的是将修改过的页面写回到磁盘上,以释放可用内存。页面按需分配是指当进程需要更多内存时,内核只会分配一部分页面,随着进程的内存使用量增加,内核会根据需求逐渐分配更多的页面。页面归还则是指当不需要的页面被内核回收时,内核会将这些页面归还给内存池,以供其他进程使用。 综上所述,Linux内存慢速申请策略通过动态页面调度、页面换入换出、页回写、页面按需分配和页面归还等技术手段来实现对系统内存的合理管理和分配,从而保证系统在内存紧张的情况下仍能维持正常的运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值