Chinaunix首页 | 论坛 | 博客
  • 博客访问: 697107
  • 博文数量: 183
  • 博客积分: 2650
  • 博客等级: 少校
  • 技术积分: 1428
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-22 17:02
文章分类
文章存档

2017年(1)

2015年(46)

2014年(4)

2013年(8)

2012年(2)

2011年(27)

2010年(35)

2009年(60)

分类: LINUX

2009-10-09 22:26:58

如上图所示,每一个框表示一个页面,大括号表示相应的位图.我们考虑一下以下几个问题.

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中的第58是空闲的.这时会把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()在下面再分析

阅读(710) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~