分类:
2011-12-16 20:41:42
原文地址:linux内存管理之伙伴系统分析(大内存分配)(续三) 作者:xgr180
min /= 2;
if (can_try_harder)
min -= min / 4;
min += (1<
if (z->free_pages < min)
continue;
page = buffered_rmqueue(z, order, gfp_mask);
if (page)
goto got_pg;
}
do_retry = 0;
if (!(gfp_mask & __GFP_NORETRY)) {
if ((order <= 3) || (gfp_mask & __GFP_REPEAT))
do_retry = 1;
if (gfp_mask & __GFP_NOFAIL)
do_retry = 1;
}
if (do_retry) {
blk_congestion_wait(WRITE, HZ/50);
goto rebalance;
}
nopage:
if (!(gfp_mask & __GFP_NOWARN) && printk_ratelimit()) {
printk(KERN_WARNING "%s: page allocation failure."
" order:%d, mode:0x%x\n",
p->comm, order, gfp_mask);
dump_stack();
}
return NULL;
got_pg:
zone_statistics(zonelist, z);
kernel_map_pages(page, 1 << order, 1);
return page;
}
从上面的分配过程,我们可以看到.整个分配过程是非常繁杂的,不过,大部份情况下,在第一次循环下就可以请求到内存了.上面这段代码中有一个重要的函数try_to_free_pages(),由于涉及的东西比较多,在分析完slab,swap address_space之后再对它进行详细的分析.请关注本站的更新.
到这里,对alloc_pages有个大概的了解,从上面的代码可以看到,从相应的zone分配内存的接口是buffered_rmqueue().代码如下:
static struct page *
buffered_rmqueue(struct zone *zone, int order, int gfp_flags)
{
unsigned long flags;
struct page *page = NULL;
int cold = !!(gfp_flags & __GFP_COLD);
if (order == 0) {
//单页面的分配
//每个cpu都维持着一个”冷”,”热”页面的内存池
struct per_cpu_pages *pcp;
//取得当前cpu对应的pcp
pcp = &zone->pageset[get_cpu()].pcp[cold];
local_irq_save(flags);
//如果剩余页低于指定的数值,就从zone中请求一大块内存,将之放入pcp
if (pcp->count <= pcp->low)
pcp->count += rmqueue_bulk(zone, 0,
pcp->batch, &pcp->list);
if (pcp->count) {
//如果有内存剩余,直接从pcp中取从页面即可
page = list_entry(pcp->list.next, struct page, lru);
list_del(&page->lru);
pcp->count--;
}
local_irq_restore(flags);
put_cpu();
}
if (page == NULL) {
//多页面分配,或者是单页面的上述分配过程失败
spin_lock_irqsave(&zone->lock, flags);
page = __rmqueue(zone, order);
spin_unlock_irqrestore(&zone->lock, flags);
}
if (page != NULL) {
BUG_ON(bad_range(zone, page));
mod_page_state_zone(zone, pgalloc, 1 << order);
prep_new_page(page, order);
if (order && (gfp_flags & __GFP_COMP))
prep_compound_page(page, order);
}
return page;
}
Pcp结构是2.6中新加入的per-cpu结点,据统计,冷热页面缓存区的存在,使整个系统效率掉高了17%.
_rmqueue()是从相应zone中取得多页面的操作,它是整个过程的核心代码,代码如下:
static struct page *__rmqueue(struct zone *zone, unsigned int order)
{
struct free_area * area;
unsigned int current_order;
struct page *page;
unsigned int index;
for (current_order = order; current_order < MAX_ORDER; ++current_order) {
//从zone中取得相应大小的free_area
area = zone->free_area + current_order;
//如果free_area为空,则从下一个空闲区分配
if (list_empty(&area->free_list))
continue;
//从空闲区中分得内存
page = list_entry(area->free_list.next, struct page, lru);
//脱链
list_del(&page->lru);
//对应页目对zone的page数组中的序号
index = page - zone->zone_mem_map;
if (current_order != MAX_ORDER-1)
//更新分配位图
MARK_USED(index, current_order, area);
//更新zone空闲页面计数
zone->free_pages -= 1UL << order;
return expand(zone, page, index, order, current_order, area);
}
return NULL;
}
static inline struct page *
expand(struct zone *zone, struct page *page,
unsigned long index, int low, int high, struct free_area *area)
{
//求得总的内框个数
unsigned long size = 1 << high;
while (high > low) {
area--;
high--;
size >>= 1;
BUG_ON(bad_range(zone, &page[size]));
list_add(&page[size].lru, &area->free_list);
//更正空闲区的页框位图
MARK_USED(index + size, high, area);
}
return page;
}
Expand()把剩余的内存归还给下一个空闲区.参数的含义如下:
zone: 管理区
page: 要归还内存的起始内框page
index: 在管理区中的序号
low: 欲分配的内框大小
high: 已经分配的内框大小
area: 空闲区
这段代码比较晦涩,用下形表示操作过程: