分类:
2011-12-16 20:41:31
原文地址:linux内存管理之伙伴系统分析(大内存分配)(续四) 作者:xgr180
如上图所示,每一个框表示一个页面,大括号表示相应的位图.我们考虑一下以下几个问题.
1:如何知道页面在相应空闲所在的分配位图位
结合以前讨论的伙伴系统分配位图相关知道,知道空闲区的每一组用一个位表示其分配情况,所以,在2^n的空闲链表中,每一个空闲块是2^n大小,每个位图表示 2*2^n个页.在zone中序号为index的页在2^n空闲链中相应的位图是 index/2*2^n = index>>(n+1).如上图中2^0空闲链中,1,2属于第一个位,3,4属于第二个位.
有了上面的知识,我们就可以分析用来改变位图相应位的宏:
#define MARK_USED(index, order, area) \
__change_bit((index) >> (1+(order)), (area)->map)
其中index是页面的序号,2^order即为area每一个空闲块的大小.这个宏对页对应的位的值进行了改变(0变成1,1变成0)
2:在一个大空闲中请求一个小空闲区,这时,大空闲的剩余部份怎么并入到小的空闲链表
如上图,如果要请求2个页面.首先它会到2^1中寻找有没有空闲页面.如果有,正好从这个链表中分配,如果没有,则要到2^2这条链表中分配,假设2^2中的第5到8是空闲的.这时会把5—8这四个页面从2^2链中脱落,更新2^2的相应分配位图.然后,再把它的半数加入到期2^1链,并更新2^1链的位.总之按照半数递减的关系放入前一个链表.按半数递减是为了把剩余内存尽量的分成大内存(因为空闲链是按2的部数增加的)
3:剩余内存归入到低链表的时候,底链表会不会合成大的空闲块?
例子如上例中.将4页面放入到2^2链的时候,有没有机会合成一个8页面空闲块?
仔细分析,不难得出:2^(n+1)每个空闲块刚好可以表示2^n位图中所表示的两个空闲块.例如上例中2^1 的第一个块,就是2^0的第位图第一个位所表示的两个空闲块.
结合前面所讨论的内容,只有同一个位图位所表示的两个空闲块都空闲的时候,才能将它合并.把剩余内存归入前一空闲链的时候,不可能满足上面所讲的要求,所以,不可能在低链上合并成高内存.
其实,只要理解了分配位图与空闲区的关系,上面的代码还是很简单的
四: free_pages()的相关实现
fastcall void __free_pages(struct page *page, unsigned int order)
{
//PageReserved(page):页面被保留或者没有分配出去
// put_page_testzero(page):把页面的引用计数减1,并判断是否为零
if (!PageReserved(page) && put_page_testzero(page)) {
//这个页没有被使用了,可能释放到伙伴系统了
if (order == 0)
//单页面的释放
free_hot_page(page);
else
//多页面的释放
__free_pages_ok(page, order);
}
}
如前面所讲述的那样,为了提高内存管理的效率,每个cpu维持了一个单页面的缓冲区.所以当释放一个单面的时候,把它释放到缓存池就好了.
我们先看一下单页面释放的代码:
void fastcall free_hot_page(struct page *page)
{
free_hot_cold_page(page, 0);
}
继续跟进:
static void fastcall free_hot_cold_page(struct page *page, int cold)
{
//page所在zone
struct zone *zone = page_zone(page);
struct per_cpu_pages *pcp;
unsigned long flags;
//只有配置了HAVE_ARCH_FREE_PAGE才会起作用
arch_free_page(page, 0);
//只有配置了CONFIG_DEBUG_PAGEALLOC才会起作用
kernel_map_pages(page, 1, 0);
inc_page_state(pgfree);
//在”磁盘高速缓存”再来讨论mapping字段
if (PageAnon(page))
page->mapping = NULL;
//DEBUG用
free_pages_check(__FUNCTION__, page);
//得到pcp
pcp = &zone->pageset[get_cpu()].pcp[cold];
local_irq_save(flags);
if (pcp->count >= pcp->high)
//如果剩余量超过了允许的最大值,把它释放到伙伴系统
pcp->count -= free_pages_bulk(zone, pcp->batch, &pcp->list, 0);
//将页面链入
list_add(&page->lru, &pcp->list);
//更新计数
pcp->count++;
local_irq_restore(flags);
put_cpu();
}
当pcp中缓存的页面超过了最大值,将pcp->batch个页面释放到伙伴系统. free_pages_bulk()在下面再分析