Chinaunix首页 | 论坛 | 博客
  • 博客访问: 163950
  • 博文数量: 26
  • 博客积分: 17
  • 博客等级: 民兵
  • 技术积分: 145
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-10 13:14
文章分类
文章存档

2014年(1)

2013年(1)

2012年(22)

2011年(2)

分类: LINUX

2013-03-08 17:01:55

摘要:本文主要讲述linux如何处理ARM cortex A9多核处理器的内存管理部分。主要包括对伙伴系统算法流程的介绍。

法律声明LINUX3.0内核源代码分析》系列文章由谢宝友()发表于http://xiebaoyou.blog.chinaunix.net,文章中的LINUX3.0源代码遵循GPL协议。除此以外,文档中的其他内容由作者保留所有版权。谢绝转载。

 

1.1.1      伙伴系统 1.1.1.1         从缓存中释放单个页面到伙伴系统

当释放单个页面时,页面首先放回到每CPU的页面缓存中,以减少自旋锁的使用。当页面缓存中的页面数量超过阀值时,再将页面放回到伙伴系统中。相应的函数是free_pcppages_bulk

 

  1. /**

  2.  * 从每CPU缓存中释放一定数量的页面到伙伴系统中。

  3.  */

  4. static void free_pcppages_bulk(struct zone *zone, int count,

  5.                     struct per_cpu_pages *pcp)

  6. {

  7.     int migratetype = 0;

  8.     int batch_free = 0;

  9.     int to_free = count;



  10.     /**

  11.      * 虽然管理区可以按照CPU节点分类,但是也可以跨CPU节点进行内存分配,因此这里需要用自旋锁保护管理区

  12.      * 使用每CPU缓存的目的,也是为了减少使用这把锁。

  13.      */

  14.     spin_lock(&zone->lock);

  15.     /* all_unreclaimable代表了内存紧张程度,释放内存后,将此标志清除 */

  16.     zone->all_unreclaimable = 0;

  17.     zone->pages_scanned = 0;/* pages_scanned代表最后一次内存紧张以来,页面回收过程已经扫描的页数。目前正在释放内存,将此清0,待回收过程随后回收时重新计数 */



  18.     while (to_free) {/* 连续释放指定数量的页面到伙伴系统 */

  19.         struct page *page;

  20.         struct list_head *list;

  21.         /* 这里是从MIGRATE_UNMOVABLE到MIGRATE_MOVABLE三个每CPU缓存链表中依次搜索一个可释放的页面,直到找到一个可以回收的缓存链表。 */

  22.         do {

  23.             batch_free++;/* batch_free是最后扫描的链表索引 */

  24.             if (++migratetype == MIGRATE_PCPTYPES)/* 只从MIGRATE_UNMOVABLE..MIGRATE_MOVABLE三个链表中选择页面。从这三个链表中循环选择回收的页面 */

  25.                 migratetype = 0;

  26.             list = &pcp->lists[migratetype];

  27.         } while (list_empty(list));/* 如果当前链表为空,则从下一个缓存链表中释放页面 */



  28.         /* This is the only non-empty list. Free them all. */

  29.         if (batch_free == MIGRATE_PCPTYPES)/* 只有一个缓存链表中有可回收页面,则从该链表中释放所有页面 */

  30.             batch_free = to_free;



  31.         do {/* 本循环从最后一个页面缓存链表中释放多个页面到伙伴系统,越靠后的缓存链表,释放的页面数量越多 */

  32.             page = list_entry(list->prev, struct page, lru);/* 取缓存链表的最后一个元素,要么是最早放到缓存中的页面(其硬件缓存的热度也最低),要么就是"冷"页,不需要考虑硬件缓存 */

  33.             /* must delete as __free_one_page list manipulates */

  34.             list_del(&page->lru);/* 将页面从链表中摘除 */

  35.             /* MIGRATE_MOVABLE list may include MIGRATE_RESERVEs */

  36.             /* 将页面释放回伙伴系统。注意这里没有用migratetype变量,而是用页面属性中的migratetype。原因请参见英文注释 */

  37.             __free_one_page(page, zone, 0, page_private(page));

  38.             trace_mm_page_pcpu_drain(page, 0, page_private(page));/* 调试代码 */

  39.         } while (--to_free && --batch_free && !list_empty(list));

  40.     }

  41.     /* 空闲页面计数 */

  42.     __mod_zone_page_state(zone, NR_FREE_PAGES, count);

  43.     spin_unlock(&zone->lock);

  44. }

 

 
1.1.1.2         释放多个页面到伙伴系统

一次性分配和释放多个页面,都不经过每CPU缓存,而是直接从伙伴系统中分配、直接返回给伙伴系统。

    向伙伴系统释放页面的函数是free_one_page,这个函数有点名不副实。它并不是指释放一个页面,而是释放多个页面,不知道内核为何取如此奇怪的名字。
  1. /**

  2.  * 将多个页面释放到伙伴系统。

  3.  */

  4. static void free_one_page(struct zone *zone, struct page *page, int order,

  5.                 int migratetype)

  6. {

  7.     spin_lock(&zone->lock);/* 获得管理区的自旋锁 */

  8.     zone->all_unreclaimable = 0;/* 只要是释放了页面,都需要将此两个标志清0,表明内存不再紧张的事实 */

  9.     zone->pages_scanned = 0;



  10.     /* 在锁的保护下,调用__free_one_page操作伙伴系统,将页面释放回伙伴系统 */

  11.     __free_one_page(page, zone, order, migratetype);

  12.     /* 管理区空闲页面计数 */

  13.     __mod_zone_page_state(zone, NR_FREE_PAGES, 1 << order);

  14.     spin_unlock(&zone->lock);/* 释放管理区自旋锁 */

  15. }

在获得管理区锁的情况下,不管是释放一个页还是多个页到伙伴系统,均使用函数__free_one_page

  1. /**
  2.  * 在获得管理区自旋锁的情况下,将页面归还给伙伴系统。
  3.  */
  4. static inline void __free_one_page(struct page *page,
  5.         struct zone *zone, unsigned int order,
  6.         int migratetype)
  7. {
  8.     unsigned long page_idx;
  9.     unsigned long combined_idx;
  10.     unsigned long uninitialized_var(buddy_idx);
  11.     struct page *buddy;

  12.     if (unlikely(PageCompound(page)))/* 要释放的页是巨页的一部分 */
  13.         if (unlikely(destroy_compound_page(page, order)))/* 解决巨页标志,如果巨页标志有问题,则退出 */
  14.             return;

  15.     VM_BUG_ON(migratetype == -1);

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

  17.     VM_BUG_ON(page_idx & ((1 << order) - 1));/* 如果被释放的页不是所释放阶的第一个页,则说明参数有误 */
  18.     VM_BUG_ON(bad_range(zone, page));/* 检查页面是否处于zone之中 */

  19.     while (order < MAX_ORDER-1) {/* 释放页以后,当前页面可能与前后的空闲页组成更大的空闲页面,直到放到最大阶的伙伴系统中 */
  20.         buddy_idx = __find_buddy_index(page_idx, order);/* 找到与当前页属于同一个阶的伙伴页面索引 */
  21.         buddy = page + (buddy_idx - page_idx);/* 计算伙伴页面的页地址 */
  22.         if (!page_is_buddy(page, buddy, order))/* 判断预期的伙伴页面是否与当前页处于同一个管理区,并且是否处于空闲状态。如果不是,则不能与该页合并为大的伙伴,退出 */
  23.             break;

  24.         /* Our buddy is free, merge with it and move up one order. */
  25.         list_del(&buddy->lru);/* 如果能够合并,则将伙伴页从伙伴系统中摘除 */
  26.         zone->free_area[order].nr_free--;/* 同时减少当前阶中的空闲页计数 */
  27.         rmv_page_order(buddy);/* 清除伙伴页的伙伴标志,因为该页会被合并 */
  28.         combined_idx = buddy_idx & page_idx;
  29.         /* 将当前页与伙伴页合并后,新的页面起始地址 */
  30.         page = page + (combined_idx - page_idx);
  31.         page_idx = combined_idx;
  32.         order++;
  33.     }
  34.     /* 设置伙伴页中第一个空闲页的阶 */
  35.     set_page_order(page, order);

  36.     /**
  37.      * 如果当前合并后的页不是最大阶的,那么将当前空闲页放到伙伴链表的最后。
  38.      * 这样,它将不会被很快被分配,更有可能与更高阶页面进行合并。
  39.      */
  40.     if ((order < MAX_ORDER-2) && pfn_valid_within(page_to_pfn(buddy))) {
  41.         struct page *higher_page, *higher_buddy;
  42.         /* 计算更高阶的页面索引及页面地址 */
  43.         combined_idx = buddy_idx & page_idx;
  44.         higher_page = page + (combined_idx - page_idx);
  45.         buddy_idx = __find_buddy_index(combined_idx, order + 1);
  46.         higher_buddy = page + (buddy_idx - combined_idx);
  47.         if (page_is_buddy(higher_page, higher_buddy, order + 1)) {/* 更高阶的页面是空闲的,属于伙伴系统 */
  48.             list_add_tail(&page->lru,
  49.                 &zone->free_area[order].free_list[migratetype]);/* 将当前页面合并到空闲链表的最后,尽量避免将它分配出去 */
  50.             goto out;
  51.         }
  52.     }

  53.     /* 更高阶的页面已经分配出去,那么将当前页面放到链表前面 */
  54.     list_add(&page->lru, &zone->free_area[order].free_list[migratetype]);
  55. out:
  56.     /* 将当前阶的空闲计数加1 */
  57.     zone->free_area[order].nr_free++;
  58. }
1.1.1.3         从伙伴系统中获得页面

从伙伴系统中分配页面的函数是buffered_rmqueue

  1. /**
  2.  * 从伙伴系统中分配页面
  3.  */
  4. static inline
  5. struct page *buffered_rmqueue(struct zone *preferred_zone,
  6.             struct zone *zone, int order, gfp_t gfp_flags,
  7.             int migratetype)
  8. {
  9.     unsigned long flags;
  10.     struct page *page;
  11.     int cold = !!(gfp_flags & __GFP_COLD);/* 如果分配参数指定了__GFP_COLD标志,则设置cold标志 */

  12. again:
  13.     if (likely(order == 0)) {/* 分配单页,需要管理每CPU页面缓存 */
  14.         struct per_cpu_pages *pcp;
  15.         struct list_head *list;

  16.         /* 这里需要关中断,因为内存回收过程可能发送核间中断,强制每个核从每CPU缓存中释放页面。而且中断处理函数也会分配单页。 */
  17.         local_irq_save(flags);
  18.         /* 取得本CPU的页面缓存对象 */
  19.         pcp = &this_cpu_ptr(zone->pageset)->pcp;
  20.         /* 根据用户指定的迁移类型,从指定的迁移缓存链表中分配 */
  21.         list = &pcp->lists[migratetype];
  22.         if (list_empty(list)) {/* 缓存为空,需要扩大缓存的大小 */
  23.             /* 从伙伴系统中摘除一批页面到缓存中,补充的页面个数由每CPU缓存的batch字段指定 */
  24.             pcp->count += rmqueue_bulk(zone, 0,
  25.                     pcp->batch, list,
  26.                     migratetype, cold);
  27.             /* 如果链表仍然为空,那么说明伙伴系统中页面也没有了,分配失败。 */
  28.             if (unlikely(list_empty(list)))
  29.                 goto failed;
  30.         }

  31.         if (cold)
  32.             /* 如果分配的页面不需要考虑硬件缓存(注意不是每CPU页面缓存),则取出链表的最后一个节点返回给上层 */
  33.             page = list_entry(list->prev, struct page, lru);
  34.         else
  35.             /* 如果要考虑硬件缓存,则取出链表的第一个页面,这个页面是最近刚释放到每CPU缓存的,缓存热度更高 */
  36.             page = list_entry(list->next, struct page, lru);

  37.         /* 将页面从每CPU缓存链表中取出,并将每CPU缓存计数减1 */
  38.         list_del(&page->lru);
  39.         pcp->count--;
  40.     } else {/* 分配的是多个页面,不需要考虑每CPU页面缓存,直接从系统中分配 */
  41.         if (unlikely(gfp_flags & __GFP_NOFAIL)) {/* __GFP_NOFAIL已经不推荐使用,不再分析 */
  42.             /*
  43.              * __GFP_NOFAIL is not to be used in new code.
  44.              *
  45.              * All __GFP_NOFAIL callers should be fixed so that they
  46.              * properly detect and handle allocation failures.
  47.              *
  48.              * We most definitely don't want callers attempting to
  49.              * allocate greater than order-1 page units with
  50.              * __GFP_NOFAIL.
  51.              */
  52.             WARN_ON_ONCE(order > 1);
  53.         }
  54.         /* 关中断,并获得管理区的锁 */
  55.         spin_lock_irqsave(&zone->lock, flags);
  56.         /* 调用__rmqueue从伙伴系统中分配页面 */
  57.         page = __rmqueue(zone, order, migratetype);
  58.         /* 这里仅仅打开自旋锁,待后面统计计数设置完毕后再开中断 */
  59.         spin_unlock(&zone->lock);
  60.         if (!page)
  61.             goto failed;
  62.         /* 已经分配了1 << order个页面,这里进行管理区空闲页面统计计数 */
  63.         __mod_zone_page_state(zone, NR_FREE_PAGES, -(1 << order));
  64.     }

  65.     /* 事件统计计数,调试用 */
  66.     __count_zone_vm_events(PGALLOC, zone, 1 << order);
  67.     zone_statistics(preferred_zone, zone, gfp_flags);
  68.     local_irq_restore(flags);/* 恢复中断 */

  69.     VM_BUG_ON(bad_range(zone, page));
  70.     /**
  71.      * 这里进行安全性检查,并进行一些善后工作。
  72.      * 如果页面标志破坏,返回的页面出现了问题,则返回试图分配其他页面。
  73.      * 内核会有这样的BUG存在吗?除了驱动或者模块有问题外?
  74.      */
  75.     if (prep_new_page(page, order, gfp_flags))
  76.         goto again;
  77.     return page;

  78. failed:
  79.     local_irq_restore(flags);
  80.     return NULL;
  81. }
这里使用了一个辅助函数rmqueue_bulk
  1. /**
  2.  * 从伙伴系统中批量取出多个页面。
  3.  * 用于从伙伴系统中批量取出页面放到每CPU页面缓存中
  4.  */
  5. static int rmqueue_bulk(struct zone *zone, unsigned int order,
  6.             unsigned long count, struct list_head *list,
  7.             int migratetype, int cold)
  8. {
  9.     int i;
  10.     
  11.     spin_lock(&zone->lock);/* 上层函数已经关了中断,这里需要操作管理区,获取管理区的自旋锁 */
  12.     for (i = 0; i < count; ++i) {/* 重复指定的次数,从伙伴系统中分配页面 */
  13.         /* 从伙伴系统中取出页面 */
  14.         struct page *page = __rmqueue(zone, order, migratetype);
  15.         /* 伙伴系统中没有空闲页了,退出 */
  16.         if (unlikely(page == NULL))
  17.             break;

  18.         /*
  19.          * Split buddy pages returned by expand() are received here
  20.          * in physical page order. The page is added to the callers and
  21.          * list and the list head then moves forward. From the callers
  22.          * perspective, the linked list is ordered by page number in
  23.          * some conditions. This is useful for IO devices that can
  24.          * merge IO requests if the physical pages are ordered
  25.          * properly.
  26.          */
  27.         if (likely(cold == 0))/* 根据调用者的要求,将页面放到每CPU缓存链表的头部或者尾部 */
  28.             list_add(&page->lru, list);
  29.         else
  30.             list_add_tail(&page->lru, list);
  31.         /* 设置页面的迁移类型 */
  32.         set_page_private(page, migratetype);
  33.         list = &page->lru;
  34.     }
  35.     /* 递减管理区的空闲页面计数。 */
  36.     __mod_zone_page_state(zone, NR_FREE_PAGES, -(i << order));
  37.     spin_unlock(&zone->lock);/* 释放管理区的自旋锁 */
  38.     return i;
  39. }
 
1.1.1.4         __rmqueue函数

 

伙伴系统分配页面的总入口是__rmqueue函数:

  1. /**
  2.  * 从管理区的伙伴系统中取出页面。该函数是分配伙伴页的真正入口。
  3.  *         zone:        从该管理区的伙伴系统中分配页面.
  4.  *        order:        要分配的页面阶数.即分配的页面数是2^order
  5.  *        migratetype:    优先从本参数指定的迁移类型中分配页面.如果失败,再从备用迁移列表中分配.
  6.  */
  7. static struct page *__rmqueue(struct zone *zone, unsigned int order,
  8.                         int migratetype)
  9. {
  10.     struct page *page;

  11. retry_reserve:
  12.     /* 优先从指定的迁移类型链表中分配页面 */
  13.     page = __rmqueue_smallest(zone, order, migratetype);

  14.     /**
  15.      * 如果满足以下两个条件,就从备用链表中分配页面:
  16.      *        快速流程没有分配到页面,需要从备用迁移链表中分配.
  17.      *        当前不是从保留的链表中分配.因为保留的链表是最后可用的链表,不能从该链表分配的话,说明本管理区真的没有可用内存了.
  18.      */
  19.     if (unlikely(!page) && migratetype != MIGRATE_RESERVE) {
  20.         /* 从备用链表中分配页面. */
  21.         page = __rmqueue_fallback(zone, order, migratetype);

  22.         /*
  23.          * Use MIGRATE_RESERVE rather than fail an allocation. goto
  24.          * is used because __rmqueue_smallest is an inline function
  25.          * and we want just one call site
  26.          */
  27.         if (!page) {/* 备用链表中没有分配到页面,看来得吃老本,从保留链表中分配页面了 */
  28.             migratetype = MIGRATE_RESERVE;
  29.             goto retry_reserve;/* 跳转到retry_reserve,从保留的链表中分配页面 */
  30.         }
  31.     }

  32.     /* 调试代码,很高兴可以飘过 */
  33.     trace_mm_page_alloc_zone_locked(page, order, migratetype);
  34.     return page;
  35. }

快速流程的处理函数是__rmqueue_smallest:

 
  1. /**
  2.  * 遍历指定迁移类型的伙伴系统链表,从链表中移动最小数量的页面返回给调用者.这是伙伴系统的快速处理流程.
  3.  *        zone:        在该管理区的伙伴系统中分配页面
  4.  *        order:        要分配的页面数量阶.
  5.  *        migratetype:    在该迁移类型的链表中获取页面
  6.  */
  7. static inline
  8. struct page *__rmqueue_smallest(struct zone *zone, unsigned int order,
  9.                         int migratetype)
  10. {
  11.     unsigned int current_order;
  12.     struct free_area * area;
  13.     struct page *page;

  14.     /* Find a page of the appropriate size in the preferred list */
  15.     /* 从指定的阶到最大阶进行遍历,直到找到一个可以分配的链表 */
  16.     for (current_order = order; current_order < MAX_ORDER; ++current_order) {
  17.         /* 找到该阶对应的空闲页面链表 */
  18.         area = &(zone->free_area[current_order]);
  19.         /* 指定迁移类型的空闲链表为空,搜索下一阶的链表 */
  20.         if (list_empty(&area->free_list[migratetype]))
  21.             continue;

  22.         /* 取出链表的第一个元素 */
  23.         page = list_entry(area->free_list[migratetype].next,
  24.                             struct page, lru);
  25.         /* 将第一个元素从链表中摘除 */
  26.         list_del(&page->lru);
  27.         /* rmv_page_order将页面的伙伴标志位清0,表示该页不再属于伙伴系统.同时将Private标志清0 */
  28.         rmv_page_order(page);
  29.         /* 当前阶的空闲计数减1 */
  30.         area->nr_free--;
  31.         /**
  32.          * 如果当前阶没有可用的空闲页,而必须将高阶的空闲页面分割,则expand将高阶的页面分割后放回伙伴系统
  33.          * 如:我们将阶为4的页面分割,并向上层返回阶为2的页面,那么,我们需要将其中阶为3,2的页面放回伙伴系统.
  34.          */
  35.         expand(zone, page, order, current_order, area, migratetype);
  36.         return page;
  37.     }

  38.     return NULL;
  39. }

辅助函数expand:

 

  1. /**
  2.  * 将高阶的页面分割成低阶的页面,并放回伙伴系统.
  3.  *        low:        分配出去的低阶
  4.  *        high:        被分割的高阶
  5.  */
  6. static inline void expand(struct zone *zone, struct page *page,
  7.     int low, int high, struct free_area *area,
  8.     int migratetype)
  9. {
  10.     unsigned long size = 1 << high;/* 要被分割的高阶页面数量 */

  11.     /**
  12.      * 我们假设将阶为4的页面组分割,并分配出去阶为2的页
  13.      * 那么我们需要向伙伴系统中分别加入阶为2,3的页面组.
  14.      * 这种情况下,high=4,low=2
  15.      */
  16.     while (high > low) {
  17.         area--;/* area,high,size分别表示本次要放回伙伴系统的链表指针,阶,当前阶的页面数量 */
  18.         high--;
  19.         size >>= 1;
  20.         /* 检查要加入伙伴系统的第一页是否在管理区的范围内 */
  21.         VM_BUG_ON(bad_range(zone, &page[size]));
  22.         /* 将第一页链入相应的迁移类型链表中 */
  23.         list_add(&page[size].lru, &area->free_list[migratetype]);
  24.         /* 该阶的空闲页面计数值加1 */
  25.         area->nr_free++;
  26.         /* 标记页面的阶 */
  27.         set_page_order(&page[size], high);
  28.     }
  29. }

 

伙伴系统的慢速处理流程是:

 

  1. /**
  2.  * 如果管理区的当前迁移链表中没有可分配的页面时,则从备用迁移链表中分配
  3.  */
  4. static inline struct page *
  5. __rmqueue_fallback(struct zone *zone, int order, int start_migratetype)
  6. {
  7.     struct free_area * area;
  8.     int current_order;
  9.     struct page *page;
  10.     int migratetype, i;

  11.     /* Find the largest possible block of pages in the other list */
  12.     /* 从最高阶搜索,这样可以尽量的将其他迁移列表中的大块分割,避免形成过多的碎片 */
  13.     for (current_order = MAX_ORDER-1; current_order >= order;
  14.                         --current_order) {
  15.         for (i = 0; i < MIGRATE_TYPES - 1; i++) {/* 遍历备用的迁移链表 */
  16.             migratetype = fallbacks[start_migratetype][i];

  17.             /* MIGRATE_RESERVE handled later if necessary */
  18.             /* 本函数不处理MIGRATE_RESERVE类型的迁移链表,如果本函数返回NULL,则上层函数直接从MIGRATE_RESERVE中分配 */
  19.             if (migratetype == MIGRATE_RESERVE)
  20.                 continue;

  21.             area = &(zone->free_area[current_order]);
  22.             /* 备用链表没有可用的页面,继续搜索下一链表 */
  23.             if (list_empty(&area->free_list[migratetype]))
  24.                 continue;

  25.             /* 取备用链表的第一个元素 */
  26.             page = list_entry(area->free_list[migratetype].next,
  27.                     struct page, lru);
  28.             /* 备用链表的当前阶计数减1 */
  29.             area->nr_free--;

  30.             /*
  31.              * If breaking a large block of pages, move all free
  32.              * pages to the preferred allocation list. If falling
  33.              * back for a reclaimable kernel allocation, be more
  34.              * aggressive about taking ownership of free pages
  35.              */
  36.             if (unlikely(current_order >= (pageblock_order >> 1)) ||/* 要分割的页面是一个大页面,则将整个页面全部迁移到当前迁移类型的链表中,这样可以避免过多的碎片 */
  37.                     start_migratetype == MIGRATE_RECLAIMABLE ||/* 目前分配的是可回收页面,这类页面有突发的特点,将页面全部迁移到可回收链表中,可以避免将其他迁移链表分割成太多的碎片 */
  38.                     page_group_by_mobility_disabled) {/* 指定了迁移策略,总是将被分割的页面迁移 */
  39.                 unsigned long pages;
  40.                 /* 将页面从当前迁移链表转换到start_migratetype指定的迁移链表中 */
  41.                 pages = move_freepages_block(zone, page,
  42.                                 start_migratetype);

  43.                 /* Claim the whole block if over half of it is free */
  44.                 /* pages是移动的页面数,如果可移动的页面数量较多,则将整个大内存块的迁移类型修改 */
  45.                 if (pages >= (1 << (pageblock_order-1)) ||
  46.                         page_group_by_mobility_disabled)
  47.                     set_pageblock_migratetype(page,
  48.                                 start_migratetype);

  49.                 migratetype = start_migratetype;
  50.             }

  51.             /* Remove the page from the freelists */
  52.             /* 将页面从伙伴伙伴系统链表中移除 */
  53.             list_del(&page->lru);
  54.             rmv_page_order(page);

  55.             /* Take ownership for orders >= pageblock_order */
  56.             if (current_order >= pageblock_order)
  57.                 change_pageblock_range(page, current_order,
  58.                             start_migratetype);

  59.             /* 将大阶的页面块中未用的部分还回伙伴系统 */
  60.             expand(zone, page, order, current_order, area, migratetype);

  61.             trace_mm_page_alloc_extfrag(page, order, current_order,
  62.                 start_migratetype, migratetype);

  63.             return page;
  64.         }
  65.     }

  66.     return NULL;
  67. }
阅读(3502) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~