Chinaunix首页 | 论坛 | 博客
  • 博客访问: 148212
  • 博文数量: 38
  • 博客积分: 1315
  • 博客等级: 准尉
  • 技术积分: 850
  • 用 户 组: 普通用户
  • 注册时间: 2011-01-06 16:19
文章分类

全部博文(38)

文章存档

2011年(38)

分类: LINUX

2011-01-10 09:31:38

    上一节我们介绍了伙伴系统的原理。本节将分析内核相关的代码。
    先看一下分配函数__rmqueue   

    参数:

1)        zonefree_area空闲页块链表所在的区。

2)        order:在哪一级的链表上申请页块。

 

static struct page *__rmqueue(struct zone *zone, unsigned int order)

{

       struct free_area * area;

       unsigned int current_order;

       struct page *page;

       /* 自申请页块的orderMAX_ORDER-1,扫描各链表 */

       for (current_order = order; current_order < MAX_ORDER; ++current_order) {

              area = zone->free_area + current_order;

              /* 如果本链表为空,即没有空闲页块,则查找更高一级的链表 */

              if (list_empty(&area->free_list))

                     continue;

              /* 找到空闲页块,获得页块的第一个页面 */

              page = list_entry(area->free_list.next, struct page, lru);

              /* 从原链表中删除 */

              list_del(&page->lru);

              /* 清除此页块的PG_buddy标志位,设置private0,为后面分解页块做准备 */

              rmv_page_order(page);

              /* 链表页块数目减一 */

              area->nr_free--;

              /* 更新zone状态 */

              __mod_zone_page_state(zone, NR_FREE_PAGES, - (1UL << order));

              /* 分解页块 */

              expand(zone, page, order, current_order, area);

              return page;

       }

 

       return NULL;

}

 

    将一个大页块分解成若干小页块。

    参数:

1)        zone:页块所在的区。

2)        page:待分解大页块的第一个页面。

3)        low:申请页块的order

4)        high:待分解页块的order

5)        area:包含待分解页块所在链表的free_area

 

static inline void expand(struct zone *zone, struct page *page,

      int low, int high, struct free_area *area)

{

       /* 初始化size为大页块的大小 */

       unsigned long size = 1 << high;

       /* 逐步将大页块分解成小页块,举例说明:假设申请256个页面的页块,待分解的是

   1024的页块,第一次循环先从1024页块分出一个512的页块,第二次循环再分出一个

   256的页块 */

       while (high > low) {

              /* 指向低一级的free_area */

              area--;

              /* 当前链表的order */

              high--;

/* 当前链表的页块大小 */

              size >>= 1;

              /* 校验页块是否有效,检查是否有空洞,页块中的页面是否都在同一个zone */

              VM_BUG_ON(bad_range(zone, &page[size]));

              /* 将高地址区的页块加入链表,上例中,第一次循环是512页块,第二次循环是剩

       下的256页块 */

              list_add(&page[size].lru, &area->free_list);

              /* 当前链表的空闲页块计数加一 */

              area->nr_free++;

              /* 设置页块的PG_buddy标志位(通过此标志位表示页块已经加入到伙伴系统

       中),并设置private为当前order */

              set_page_order(&page[size], high);

       }

}

 

 接下来分析一下释放页块函数__free_one_page。

    参数:

1)        page:待释放的页块。

2)        zone:页块所在的区

3)        order:页块所在链表的order

 

static inline void __free_one_page(struct page *page,

              struct zone *zone, unsigned int order)

{

       unsigned long page_idx;

       int order_size = 1 << order;

      /* 大页面相关,如果内核采用了大页面机制,比如2MB的页面大小,那么就需要由5124KB的小页

      面组合成一个compound大页面,分配时需设置小页面的PG_compound标志位,释放时需要清除

      这个标志位。*/

       if (unlikely(PageCompound(page)))

              destroy_compound_page(page, order);

       /* 将与伙伴算法无关的高位屏蔽 */

       page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1);

       /* 页块索引是否是2order的整数倍 */

       VM_BUG_ON(page_idx & (order_size - 1));

       /* 校验页块是否有效,检查是否有空洞,页块中的页面是否都在同一个zone */

       VM_BUG_ON(bad_range(zone, page));

       /* 更新zone的空闲页状态 */

       __mod_zone_page_state(zone, NR_FREE_PAGES, order_size);

       /* 按照伙伴算法合并伙伴 */

       while (order < MAX_ORDER-1) {

              unsigned long combined_idx;

              struct free_area *area;

              struct page *buddy;

              /* 查找待释放页块的伙伴 */

              buddy = __page_find_buddy(page, page_idx, order);

              /* 如果伙伴校验失败,说明没有伙伴,退出循环 */

              if (!page_is_buddy(page, buddy, order))

                     break;            /* Move the buddy up one level. */

              /* 将伙伴从原链表上删除 */

              list_del(&buddy->lru);

              /* 将这个order级别的空闲页块个数减一 */

              area = zone->free_area + order;

              area->nr_free--;

             /* 为下面的合并做准备,清除伙伴的PG_buddy标志位,并设置伙伴的private成员为

       0 */

              rmv_page_order(buddy);

              /* 利用伙伴算法公式计算合并后父节点的页块索引 */

              combined_idx = __find_combined_index(page_idx, order);

              /* 获得合并后页块的第一个页面 */

              page = page + (combined_idx - page_idx);

              /* 更新当前页块索引为父节点的索引 */

              page_idx = combined_idx;

              /* 继续在上一级order链表中应用伙伴合并算法 */

              order++;

       }

       /* 设置页块的PG_buddy标志位(通过此标志位表示页块已经加入到伙伴系统中),并

    设private为当前order */

       set_page_order(page, order);

       /* 将合并后页块的第一个页面加入到order级链表中 */

       list_add(&page->lru, &zone->free_area[order].free_list);

       /* 更新链表的空闲页块计数 */

       zone->free_area[order].nr_free++;

}

 

伙伴查找函数__page_find_buddy:

    参数:

1)        page:给定的页块。

2)        page_idx:给定页块的索引。

3)        order:页块所在链表的order

 

static inline struct page *

__page_find_buddy(struct page *page, unsigned long page_idx, unsigned int order)

{

       /* 利用伙伴算法公式计算伙伴 */

       unsigned long buddy_idx = page_idx ^ (1 << order);

       /*比如01为伙伴,0为小伙伴,1为大伙伴。buddy_idx大于page_idx时,给出的是小伙

    伴,计算出的是大伙伴,buddy_idx小于page_idx时,给出的是大伙伴,计算出的是小

    伙伴。*/

       return page + (buddy_idx - page_idx);

}

 

    找到伙伴后,还要做一下校验

    参数:

1)        page:给定的页块。

2)        buddy:伙伴页块的索引。

3)        order:页块所在链表的order

 

static inline int page_is_buddy(struct page *page, struct page *buddy,

                                                        int order)

{

#ifdef CONFIG_HOLES_IN_ZONE

       /* 伙伴是否是空洞 */

       if (!pfn_valid(page_to_pfn(buddy)))

              return 0;

#endif

       /* 伙伴是否在同一个zone */

       if (page_zone_id(page) != page_zone_id(buddy))

              return 0;

       /* 通过检查PG_buddy 标志位判断buddy是否在伙伴系统中,并且buddy是否在order级的

    链表中,pageprivate成员存放页块所在链表的order*/

       if (PageBuddy(buddy) && page_order(buddy) == order) {

              BUG_ON(page_count(buddy) != 0);

              return 1;

       }

       return 0;

}

 

 

 

 

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