分类:
2012-05-18 11:38:20
原文地址:linux内存管理之伙伴系统分析(大内存分配)(续五) 作者:xgr180
再来看一个多页面的释放操作,调用__free_pages_ok(),代码如下:
//释放的起始页面:page
//页面个数:2^order
void __free_pages_ok(struct page *page, unsigned int order)
{
//声时并初始化一个链表
LIST_HEAD(list);
int i;
arch_free_page(page, order);
mod_page_state(pgfree, 1 << order);
for (i = 0 ; i < (1 << order) ; ++i)
free_pages_check(__FUNCTION__, page + i);
list_add(&page->lru, &list);
kernel_map_pages(page, 1<
free_pages_bulk(page_zone(page), 1, &list, order);
}
它的核心处理都是在free_pages_bulk()完成的
//cout:释放的次数
//order:每次释放2^order个页面
//例如:要释放x个单页面,count = x order=0
//释放2^x大小的连续页面 count = 1 order = x
static int
free_pages_bulk(struct zone *zone, int count,
struct list_head *list, unsigned int order)
{
unsigned long flags;
struct free_area *area;
struct page *base, *page = NULL;
int ret = 0;
base = zone->zone_mem_map;
//找到要释放大小页面的空闲链
area = zone->free_area + order;
spin_lock_irqsave(&zone->lock, flags);
zone->all_unreclaimable = 0;
zone->pages_scanned = 0;
//如果页面释放完了,或者循环次数已尽
while (!list_empty(list) && count--) {
page = list_entry(list->prev, struct page, lru);
/* have to delete it as __free_pages_bulk list manipulates */
list_del(&page->lru);
__free_pages_bulk(page, base, zone, area, order);
ret++;
}
spin_unlock_irqrestore(&zone->lock, flags);
return ret;
}
调用__free_pages_bulk(),代码如下:
参数含义:
Page:要释放的超始page
Base:对应zone的页数组的首地址
Zone:
Area:将页面释放到此空闲区
Order:释放的连续空闲区大小
static inline void __free_pages_bulk (struct page *page, struct page *base,
struct zone *zone, struct free_area *area, unsigned int order)
{
unsigned long page_idx, index, mask;
if (order)
destroy_compound_page(page, order);
//假设order = 3
//mask = 1111 1000
mask = (~0UL) << order;
//取得页在zone中页数组中的序号
page_idx = page - base;
//~mask:0000 0111
//判断page_idx是不是order位对齐的
//结合前面alloc所分析的,不难得出在2^order大小空闲区中.
//空闲块的首地址必须是2^order的倍数
if (page_idx & ~mask)
BUG();
//得到page在链表中的分配位图对应位,前面已经介绍过
index = page_idx >> (1 + order);
//更新zone 的空闲区统计计数
zone->free_pages += 1 << order;
//循环,合并内存
while (order < MAX_ORDER-1) {
struct page *buddy1, *buddy2;
BUG_ON(area >= zone->free_area + MAX_ORDER);
//判断相邻块是否空闲.前面说过,为0的时候,是两者都空闲或//两者都分配出去了
if (!__test_and_change_bit(index, area->map))
/*
* the buddy page is still allocated.
*/
break;
/* Move the buddy up one level. */
//相邻块是空闲的
//它的邻居块
buddy1 = base + (page_idx ^ (1 << order));
//它自已
buddy2 = base + page_idx;
//判断是否超过zone所允许的page范围
BUG_ON(bad_range(zone, buddy1));
BUG_ON(bad_range(zone, buddy2));
//将其从现有的空闲链中脱落
list_del(&buddy1->lru);
//到它的上一级,判断是否有能合并的内存块
mask <<= 1;
order++;
area++;
index >>= 1;
page_idx &= mask;
}
list_add(&(base + page_idx)->lru, &area->free_list);}