mm

memory management
http://blog.csdn.net/yunsongice/archive/2010/01/22/5225155.aspx



如有问题,欢迎一起讨论:-)

struct free_area{
    struct list_head  free_list; /*用于连接某一大小的空闲页框*/
    unsigned long    *map;    /*涉及到上面大小的位图,每两个的对象之间共享一位*/
};

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++)
    {
        area = zone->area + current_order;
        /*寻找2^order链表,如果此链表为空,则进行下一个链表*/
        if(list_empty(&area->free_list))
            continue;

        /*找到free_list链表头部页框,然后删除2^current_order大小的页框*/
        page = list_entry(area->free_list.next,struct page,lru);
        list_del(&page->lru);
        index = page - zone->zone_mem_map;       
        if(current_order != MAX_ORDER -1)
            MARK_USED(index, current_order,area);
        zone->free_pages -= 1UL << order;
        /*调整free_list链表*/
        return expand(zone,page,index,order,current_order,area);
    }   
    return NULL;
}

#define MARK_USED(index,order,area)\
    __change_bit( (index)>>(1 + (order)),(area)->map )
/*(index) >> (1 + (order))是index/(2*2^order)因为两个大小相同的块共享一位
所以要除以2*/

static __inline__ void __change_bit(unsigned long nr, volatile void *addr)
{
    int *m = ( (int *)addr) + (nr >> 5); /*每个整型数据有32位图,找到第几个整型数据*/
    *m ^= 1 << (nr & 31); /*给对应位置位*/
}

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--;        /*current_order--*/
        size >>= 1;    /*下一个数组链表中的内存块大小*/
        list_add(&page[size].lru, &area->free_list); /*加入链表*/
        MARK_USED(index + size,high,area);    /**/
    }
    return page;
}

下面是个牛人讲解位图的使用, 觉得应该是至今找的最全的了:

http://blog.csdn.net/ruixj/archive/2009/12/26/5076398.aspx

在page_init中 通过调用pagetable_init();建立了页目录表和页表;通过调用zone_sizes_init(); 函数建立了Node, Zone,Page的分层的结构,并且初始化了伙伴算法的基本数据结构(为每个zone的free_area 的map字段分配内存,并且初始化为0)。

位图大小

free_area[ 0] . map位图大小= ( ( size- 1) / 2) / 8= ( size- 1) > > ( 0+ 1+ 3)
free_area[ 1] . map位图大小= ( ( size- 1) / 4) / 8= ( size- 1) > > ( 1+ 1+ 3)
. . .
free_area[ 9] . map位图大小= ( ( size- 1) / 1024) / 8= ( size- 1) > > ( 9+ 1+ 3)


这里之所以要在order的基础上要加1,是因为每一位bit管理着两块内存块。

然后在mem_init中调用__free_pages_ok来将某个zone中的所有页框组织到free_area中。下面分析一下__free_pages_ok。

在分析之前先说一下buddy 算法的原理:

 "位图中的一个位代表相邻的两个伙伴页当前被使用情况" , 这样1个位顶2个位来用, 就可以管理其2倍大小的size- 1个内存页了.


我们知道偶数号位图- A和+ 1后的紧邻奇数号位图- B, 它们是伙伴, 它们两个共用位图A> > 1, 如:

下面索引的意思是第几块的意思.
对于order= 0, 索引0- [ 0] , 索引1- [ 1] 是伙伴 - - - - - - - - - - - - - - - - 共用位图0
              索引2- [ 2] , 索引3- [ 3] 是伙伴 - - - - - - - - - - - - - - - - 共用位图1
               . . . . . .
              索引2* n- [ 2* n] , 索引2* n+ 1- [ 2* n+ 1] 是伙伴 - - - - - - - - - - - - - - - - 共用位图n

对于order= 1, [ 索引0- 页地址0, 索引1- 页地址2=2*1] 是伙伴 - - - - - - - - - - - - - - - - 共用位图0
              [ 索引2- 页地址4=2*2, 索引3- 页地址6=2*3] 是伙伴 - - - - - - - - - - - - - - - - 共用位图1
               . . . . . .
              [ 索引n- 页地址2n, 索引n+ 1- 页地址2( n+ 1) ] 是伙伴- - - - - - - - - - - - - - - - 共用位图n

对于order= 2, [ 索引0- 页地址0, 索引1- 页地址4=4*1 ] 是伙伴 - - - - - - - - - - - - - - - - 共用位图0
              [ 索引2- 页地址8=4*2, 索引3- 页地址12=4*1] 是伙伴 - - - - - - - - - - - - - - 共用位图1
               . . . . . .
             [ 索引n- 页地址4n, 索引n+ 1- 页地址4( n+ 1) ] 是伙伴- - - - - - - - - - - - -   共用位图n


从上图可以看到伙伴是固定的,不会变的。在order为任何大小的时候,相邻的两块都是伙伴,都是0和1,2和3,4和5,.....这样一对一对下去的。

就是说0和2绝对不会是伙伴。


所以用n个位图就可以描述2n个单元了.
拿第80位map_bit80来说, 一开始map_bit80管理的两个伙伴都没有被分配出去, 这时map_bit80= 0,
由于需要现在A页块要被分配出去了, A分配出去的同时和1进行异或操作map_bit80= map_bit80^1= 1,
现在有两种情况:
1. B不被分配出去, A此时需要释放了, A开始"找朋友" ,
  首先计算A和朋友B对应的位图, 因为map_bit80此时为1, 所以与1异或之后map_bit80= map_bit80^1= 0,
  之后判断异或之后的结果map_bit80, 如果异或结果为0, 表明A的"朋友" , 存在, "找朋友" 顺利完成,
  将A和它的朋友B合并成大空间A+ B, 放入orde= order+ 1, 上, 通过循环, 此时order已经加1, 继续检测加1后
  order的朋友是否可以继续合并, 这样一直合并到MAX_ORDER- 1为止.
2. B被分配出去, 此时A仍在使用, B对共用位图map_bit80同样进行异或操作map_bit80= map_bit80^1= 0,
3. B和A都被分配出去了, 此时其中有一个要释放, 比如A, 它开始"找朋友" , 我们知道它肯定找不到朋友, 因为B也被分配出去了,
  那A是怎么知道B不存在呢, 还是异或操作, 两个都被分配出去了, 所以当前map_bit80= 0, 经过异或之后
  map_bit80= map_bit80^1= 1, 结果为1, 表示"伙伴" 不存在, 那么不用合并, 直接回收即可.

总结: 不论是"申请分配" 动作还是"释放回收" 动作, 都会对共用位图进行异或操作, 之所以这样来做, 目的不在于"申请分配" 上,
     而是在"释放回收" 上, 当回收时, 通过异或结果来判断, 当前回收页的伙伴是否已经空闲的存在, 结果为0, 表示可以"合并成大页" ,
     结果为1, 表示"伙伴" 忙, 所以位图存在的主要意义是"能够将小块内存合并成大块内存".


作者: tryandy   发布时间: 2010-09-06