alloc_pages函数分析
static inline struct page *
alloc_pages(gfp_t gfp_mask, unsigned int order)
{
return alloc_pages_current(gfp_mask, order);
}
alloc_pages
调用关系:
static inline struct page *alloc_pages(gfp_t gfp_mask, unsigned int order)
struct page *alloc_pages_current(gfp_t gfp, unsigned order)
struct page *__alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, int preferred_nid,nodemask_t *nodemask)
所以分析alloc_pages
的实现需要分析__alloc_pages_nodemask
这个函数.
__alloc_pages_nodemask:
unsigned int alloc_flags = ALLOC_WMARK_LOW;
1 允许分配内存的判断条件为低水位.
if (unlikely(order >= MAX_ORDER)) {
WARN_ON_ONCE(!(gfp_mask & __GFP_NOWARN));
return NULL;
}
2 伙伴系统最大内存块大小为2(MAX_ORDER-1) 个页面,如果MAX_ORDER = 11 ,一页大小为4K则最大内存块 4*1024k = 4M大小.
prepare_alloc_pages(gfp_mask, order, preferred_nid, nodemask, &ac, &alloc_mask, &alloc_flags)
prepare_alloc_pages
的功能计算相关的信息,并保存到ac
这个变量里面,ac
是一个*struct* alloc_context
struct alloc_context {
struct zonelist *zonelist;
nodemask_t *nodemask;
struct zoneref *preferred_zoneref;
int migratetype;
enum zone_type high_zoneidx;
bool spread_dirty_pages;
};
finalise_ac(gfp_mask, &ac);
3 使用finalise_ac
确定,首选的zone.
alloc_flags |= alloc_flags_nofragment(ac.preferred_zoneref->zone, gfp_mask);
4 使用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_idx(zone) != ZONE_NORMAL)
goto out;
/*
* If ZONE_DMA32 exists, assume it is the one after ZONE_NORMAL and
* the pointer is within zone->zone_pgdat->node_zones[]. Also assume
* on UMA that if Normal is populated then so is DMA32.
*/
BUILD_BUG_ON(ZONE_NORMAL - ZONE_DMA32 != 1);
if (nr_online_nodes > 1 && !populated_zone(--zone))
goto out;
out:
#endif /* CONFIG_ZONE_DMA32 */
return alloc_flags;
}
5 如果首选的zone是高端的zone,如ZONE_NORMAL
,过早的使用了低端的zone将会造成内存短缺的问题,所以当发生碎片化的时候应该尽可能的从ZONE_NORMAL
中的其它迁移内型链表中挪用一些空闲的内存,这样会比过早的使用低端的zone会好很多.
page = get_page_from_freelist(alloc_mask, order, alloc_flags, &ac);
if (likely(page))
goto out;
使用get_page_from_freelist
进行第一次分配尝试(空闲列表里面能够满足这次的分配需求),如果成功了,就直接返回内存块的第一个页面的page,当然如果分配失败了就使用下面这个函数
page = __alloc_pages_slowpath(alloc_mask, order, &ac);
进入慢速路径进行页面分配.