不管是快速分配还是慢速分配,实际分配内存的都是 buffered_rmqueue()函数,其他的都是在选择从哪个地方来分配比较合适;
还是先来说说各个参数:
struct zone *preferred_zone 表示分配所能接受的最大zone类型
struct zone *zone 表示就在该zone上分配内存;
int order 表示分配页的阶数
gfp_t gfp_flags 分配的标识
page = buffered_rmqueue(preferred_zone, zone, order,
gfp_mask, migratetype);
/*
* Really, prep_compound_page() should be called from __rmqueue_bulk(). But
* we cheat by calling it from here, in the order > 0 path. Saves a branch
* or two.
*/
static inline
struct page *buffered_rmqueue(struct zone *preferred_zone,
struct zone *zone, int order, gfp_t gfp_flags,
int migratetype)
{
unsigned long flags;
struct page *page;
int cold = !!(gfp_flags & __GFP_COLD);//是否指定冷热页
again:
if (likely(order == 0)) {//分配单页
struct per_cpu_pages *pcp;
struct list_head *list;
local_irq_save(flags);//禁止本地CPU中断,禁止前先保存中断状态
pcp = &this_cpu_ptr(zone->pageset)->pcp;//获取到cpu高速缓存页
list = &pcp->lists[migratetype];//根据迁移类型,得到高速缓存区的freelist
if (list_empty(list)) {//空的,高速缓存没有数据;这可能是上次获取的cpu高速缓存迁移类型和这次不一样
pcp->count += rmqueue_bulk(zone, 0,
pcp->batch, list,
migratetype, cold);//该函数向高速缓存中添加内存页,具体分析见文章后面
if (unlikely(list_empty(list)))
goto failed;
}
if (cold)
page = list_entry(list->prev, struct page, lru);
else
page = list_entry(list->next, struct page, lru);
list_del(&page->lru);
pcp->count--;
} else {
if (unlikely(gfp_flags & __GFP_NOFAIL)) {
/*
* __GFP_NOFAIL is not to be used in new code.
*
* All __GFP_NOFAIL callers should be fixed so that they
* properly detect and handle allocation failures.
*
* We most definitely don't want callers attempting to
* allocate greater than order-1 page units with
* __GFP_NOFAIL.
*/
WARN_ON_ONCE(order > 1);
}
spin_lock_irqsave(&zone->lock, flags);
page = __rmqueue(zone, order, migratetype);
spin_unlock(&zone->lock);
if (!page)
goto failed;
__mod_zone_freepage_state(zone, -(1 << order),
get_pageblock_migratetype(page));
}
__count_zone_vm_events(PGALLOC, zone, 1 << order);
zone_statistics(preferred_zone, zone, gfp_flags);
local_irq_restore(flags);
VM_BUG_ON(bad_range(zone, page));
if (prep_new_page(page, order, gfp_flags))
goto again;
return page;
failed:
local_irq_restore(flags);
return NULL;
}
struct zone结构体中有个 struct per_cpu_pageset __percpu *pageset; 成员,该成员用于冷热分配器,热页表示已经在cpu的高速缓存中了;
struct per_cpu_pageset {
struct per_cpu_pages pcp;
#ifdef CONFIG_NUMA
s8 expire;
#endif
#ifdef CONFIG_SMP
s8 stat_threshold;
s8 vm_stat_diff[NR_VM_ZONE_STAT_ITEMS];
#endif
};
cpu缓存页数组
struct per_cpu_pages {
int count; /* number of pages in the list */列表中页数
int high; /* high watermark, emptying needed */列表页数的上限
int batch; /* chunk size for buddy add/remove */添加和删除页时,一次操作多少页。不是单页删除和填充的,而是以该单位页来操作的
/* Lists of pages, one per migrate type stored on the pcp-lists */
struct list_head lists[MIGRATE_PCPTYPES];//迁移类型的链表
};
从伙伴系统中得到页,然后填充到cpu的高速缓存中