页面迁移其实是伙伴管理算法中的一部分,鉴于其特殊性,特地另行分析。它是2007年的时候,2.6.24内核版本开发时,新增碎片减少策略(the fragmentation reduction strategy)所引入的。该策略也称之为反碎片技术(anti-gragmentation)。

<span style="font-family: 宋体; -ms-word-wrap: break-word;">根据《深入<span style="-ms-word-wrap: break-word;">linux</span>内核架构》的描述,反碎片的由来是因为<span style="-ms-word-wrap: break-word;">Linux</span>内存管理长期存在一个问题:系统启动并长期运行后,物理内存将会产生很多碎片。暂且假设内存页面数为<span style="-ms-word-wrap: break-word;">60</span>,则长期运行后,其页面的使用情况可能将会如下图(灰色为已分配)。

<span style="-ms-word-wrap: break-word;">![](http://blog.chinaunix.net/attachment/201504/7/26859697_1428337108n4S9.png)</span></span>

&nbsp;

<span style="font-family: 宋体; -ms-word-wrap: break-word;">虽然其未被分配的页面仍有<span style="-ms-word-wrap: break-word;">25%</span>,但能够申请到的最大页面仅为一页。不过这对用户空间是没有影响的,主要是由于用户态的内存是通过页面映射而得到的。所以不在乎具体的物理页面分布,其仍是可以将其映射为连续的一块内存提供给用户态程序使用。于是用户态可以感知的内存则如下。

<span style="-ms-word-wrap: break-word;">![](http://blog.chinaunix.net/attachment/201504/7/26859697_1428337123cqhS.png)</span></span>

&nbsp;

<span style="font-family: 宋体; -ms-word-wrap: break-word;">但是对于内核态,碎片则是个严肃的问题,因为大部分物理内存都直接映射到内核的永久映射区里面。如果真的存在碎片,将真的如第一张图所示,无法映射到比一页更大的内存,这长期是<span style="-ms-word-wrap: break-word;">linux</span>的短板之一。于是为了解决该问题,则引入了反碎片。</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">linux</span><span style="font-family: 宋体; -ms-word-wrap: break-word;">内核在内存管理时,将已分配的页面划分为三种类型:</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">不可移动页</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">&mdash;&mdash;该类型页面在内存中位置固定,不可移动。内核核心大部分内存属于该类型;</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">可回收页</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">&mdash;&mdash;不能够直接移动,但是可以删除,而内容则可以从某些源重新生成。如文件数据映射的页面则归属此类;</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">可移动页</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">&mdash;&mdash;可以随意移动,分配给用户态程序运行的用户空间页面则为该类。由于是通过页面映射而得,将其复制到新位置后,更新映射表项,重新映射,应用程序是不感知的。</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">页面的可迁移性则取决于它属于哪一类。而内核使用的反碎片技术则是基于将具有相同可移动性的页分组的思想来实现的。当出现碎片的情况时,可移动页面将会迁移,将为申请者腾出所需的连续页面空间,由此避免了空闲页面空间过于零碎而无法申请到大块连续内存。也由此,不可移动页面不允许在可移动页面中申请,避免因不可迁移而导致碎片。</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">其中具体迁移类型在头文件<span style="-ms-word-wrap: break-word;">include/linux/mmzone.h</span>中定义了:</span>


1. 【file:/include/linux/mmzone.h】
2. enum {
3.     MIGRATE_UNMOVABLE,
4.     MIGRATE_RECLAIMABLE,
5.     MIGRATE_MOVABLE,
6.     MIGRATE_PCPTYPES, / the number of types on the pcp lists /
7.     MIGRATE_RESERVE = MIGRATE_PCPTYPES,
8. #ifdef CONFIG_CMA
9.     /
10.       MIGRATE_CMA migration type is designed to mimic the way
11.       ZONE_MOVABLE works. Only movable pages can be allocated
12.       from MIGRATE_CMA pageblocks and page allocator never
13.       implicitly change migration type of MIGRATE_CMA pageblock.
14.      
15.       The way to use it is to change migratetype of a range of
16.       pageblocks to MIGRATE_CMA which can be done by
17.       __free_pageblock_cma() function. What is important though
18.       is that a range of pageblocks must be aligned to
19.       MAX_ORDER_NR_PAGES should biggest page be bigger then
20.       a single pageblock.
21.      /
22.     MIGRATE_CMA,
23. #endif
24. #ifdef CONFIG_MEMORY_ISOLATION
25.     MIGRATE_ISOLATE, / can't allocate from here */
26. #endif
27.     MIGRATE_TYPES
28. };
<span style="font-family: 宋体; -ms-word-wrap: break-word;">各类型的说明则为:</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">MIGRATE_UNMOVABLE</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">&mdash;&mdash;在内存当中有固定的位置,不能移动。内核的核心分配的内存大多属于这种类型;</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">MIGRATE_RECLAIMABLE</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">&mdash;&mdash;不能直接移动,但可以删除,其内容页可以从其他地方重新生成,例如,映射自文件的数据属于这种类型,针对这种页,内核有专门的页面回收处理;</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">MIGRATE_MOVABLE</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">&mdash;&mdash;可以随意移动,用户空间应用程序所用到的页属于该类别。它们通过页表来映射,如果他们复制到新的位置,页表项也会相应的更新,应用程序不会注意到任何改变;</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">MIGRATE_PCPTYPES</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">&mdash;&mdash;是<span style="-ms-word-wrap: break-word;">per_cpu_pageset</span>,即用来表示每<span style="-ms-word-wrap: break-word;">CPU</span>页框高速缓存的数据结构中的链表的迁移类型数目;</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">MIGRATE_RESERVE</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">&mdash;&mdash;保留页,是在前三种的列表中都没用可满足分配的内存块时,就可以从<span style="-ms-word-wrap: break-word;">MIGRATE_RESERVE</span>分配;</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">MIGRATE_CMA</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">&mdash;&mdash;连续内存分配,用于避免预留大块内存导致系统可用内存减少而实现的,即当驱动不使用内存时,将其分配给用户使用,而需要时则通过回收或者迁移的方式将内存腾出来。</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">MIGRATE_ISOLATE</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">&mdash;&mdash;用于跨越<span style="-ms-word-wrap: break-word;">NUMA</span>节点移动物理内存页,该索引的页不能分配,在大型系统上,它有益于将物理内存页移动到接近于是用该页最频繁地<span style="-ms-word-wrap: break-word;">CPU</span>;</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">MIGRATE_TYPES</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">&mdash;&mdash;表示迁移类型的数目。</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">至于迁移类型的页面管理实际上采用的还是伙伴管理算法的管理方式,内存管理区<span style="-ms-word-wrap: break-word;">zone</span>的结构里面的<span style="-ms-word-wrap: break-word;">free_area</span>是用于管理各阶内存页面,而其里面的<span style="-ms-word-wrap: break-word;">free_list</span>则是对各迁移类型进行区分的链表。回顾内存页面释放的函数<span style="-ms-word-wrap: break-word;">__free_pages</span>,其将空闲页面挂回去的时候,是做了迁移类型区分的。也就是意味着页面迁移类型是伴随着伙伴管理算法的内存管理构建,根据迁移类型进行分而治之初始化。</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">那么各种迁移类型的页面分配是如何运转的?</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">页面分配函数入口<span style="-ms-word-wrap: break-word;">__alloc_pages()</span>:</span>


1. 【file:/include/linux/gfp.h】
2. static inline struct page
3. __alloc_pages(gfp_t gfp_mask, unsigned int order,
4.         struct zonelist zonelist)
5. {
6.     return __alloc_pages_nodemask(gfp_mask, order, zonelist, NULL);
7. }
<span style="text-align: left; color: rgb(102, 102, 102); text-transform: none; text-indent: 0px; letter-spacing: normal; font-family: 宋体, Arial; font-size: 16px; font-style: normal; font-weight: normal; word-spacing: 0px; float: none; display: inline !important; white-space: normal; orphans: 2; widows: 2; background-color: rgb(255, 255, 255); font-variant-ligatures: normal; font-variant-caps: normal; -webkit-text-stroke-width: 0px;">&nbsp;</span><span style="text-align: left; color: rgb(102, 102, 102); text-transform: none; line-height: 1.5; text-indent: 21pt; letter-spacing: normal; font-family: 宋体; font-size: 16px; font-style: normal; font-weight: normal; word-spacing: 0px; white-space: normal; -ms-word-wrap: break-word; orphans: 2; widows: 2; background-color: rgb(255, 255, 255); font-variant-ligatures: normal; font-variant-caps: normal; -webkit-text-stroke-width: 0px;">其首入参在</span><span style="text-align: left; color: rgb(102, 102, 102); text-transform: none; line-height: 1.5; text-indent: 21pt; letter-spacing: normal; font-family: 宋体; font-size: 16px; font-style: normal; font-weight: normal; word-spacing: 0px; white-space: normal; -ms-word-wrap: break-word; orphans: 2; widows: 2; background-color: rgb(255, 255, 255); font-variant-ligatures: normal; font-variant-caps: normal; -webkit-text-stroke-width: 0px;">__alloc_pages_nodemask()</span><span style="text-align: left; color: rgb(102, 102, 102); text-transform: none; line-height: 1.5; text-indent: 21pt; letter-spacing: normal; font-family: 宋体; font-size: 16px; font-style: normal; font-weight: normal; word-spacing: 0px; white-space: normal; -ms-word-wrap: break-word; orphans: 2; widows: 2; background-color: rgb(255, 255, 255); font-variant-ligatures: normal; font-variant-caps: normal; -webkit-text-stroke-width: 0px;">里面会经过</span><span style="text-align: left; color: rgb(102, 102, 102); text-transform: none; line-height: 1.5; text-indent: 21pt; letter-spacing: normal; font-family: 宋体; font-size: 16px; font-style: normal; font-weight: normal; word-spacing: 0px; white-space: normal; -ms-word-wrap: break-word; orphans: 2; widows: 2; background-color: rgb(255, 255, 255); font-variant-ligatures: normal; font-variant-caps: normal; -webkit-text-stroke-width: 0px;">allocflags_to_migratetype(gfp_mask)</span><span style="text-align: left; color: rgb(102, 102, 102); text-transform: none; line-height: 1.5; text-indent: 21pt; letter-spacing: normal; font-family: 宋体; font-size: 16px; font-style: normal; font-weight: normal; word-spacing: 0px; white-space: normal; -ms-word-wrap: break-word; orphans: 2; widows: 2; background-color: rgb(255, 255, 255); font-variant-ligatures: normal; font-variant-caps: normal; -webkit-text-stroke-width: 0px;">转换获取到</span><span style="text-align: left; color: rgb(102, 102, 102); text-transform: none; line-height: 1.5; text-indent: 21pt; letter-spacing: normal; font-family: 宋体; font-size: 16px; font-style: normal; font-weight: normal; word-spacing: 0px; white-space: normal; -ms-word-wrap: break-word; orphans: 2; widows: 2; background-color: rgb(255, 255, 255); font-variant-ligatures: normal; font-variant-caps: normal; -webkit-text-stroke-width: 0px;">申请页面的类型。该迁移类型会在其内部调用函数</span><span style="text-align: left; color: rgb(102, 102, 102); text-transform: none; line-height: 1.5; text-indent: 21pt; letter-spacing: normal; font-family: 宋体; font-size: 16px; font-style: normal; font-weight: normal; word-spacing: 0px; white-space: normal; -ms-word-wrap: break-word; orphans: 2; widows: 2; background-color: rgb(255, 255, 255); font-variant-ligatures: normal; font-variant-caps: normal; -webkit-text-stroke-width: 0px;">__rmqueue()</span><span style="text-align: left; color: rgb(102, 102, 102); text-transform: none; line-height: 1.5; text-indent: 21pt; letter-spacing: normal; font-family: 宋体; font-size: 16px; font-style: normal; font-weight: normal; word-spacing: 0px; white-space: normal; -ms-word-wrap: break-word; orphans: 2; widows: 2; background-color: rgb(255, 255, 255); font-variant-ligatures: normal; font-variant-caps: normal; -webkit-text-stroke-width: 0px;">中使用。</span>


1. 【file:/mm/page_alloc.c】
2. /
3.   Do the hard work of removing an element from the buddy allocator.
4.   Call me with the zone->lock already held.
5.  /
6. static struct page __rmqueue(struct zone zone, unsigned int order,
7.                         int migratetype)
8. {
9.     struct page page;
10.  
11. retry_reserve:
12.     page = rmqueue_smallest(zone, order, migratetype);
13.  
14.     if (unlikely(!page) && migratetype != MIGRATE_RESERVE) {
15.         page = rmqueue_fallback(zone, order, migratetype);
16.  
17.         /
18.           Use MIGRATE_RESERVE rather than fail an allocation. goto
19.           is used because __rmqueue_smallest is an inline function
20.           and we want just one call site
21.          /
22.         if (!page) {
23.             migratetype = MIGRATE_RESERVE;
24.             goto retry_reserve;
25.         }
26.     }
27.  
28.     trace_mm_page_alloc_zone_locked(page, order, migratetype);
29.     return page;
30. }
<span style="font-family: 宋体; -ms-word-wrap: break-word;">前面分析可以知道<span style="-ms-word-wrap: break-word;">__rmqueue_smallest()</span>仅是在指定迁移类型下自底向上进行各阶遍历查找所需的空闲页面,而据上代码其如果在指定迁移类型下分配失败,且类型不为<span style="-ms-word-wrap: break-word;">MIGRATE_RESERVE</span>时,将会调用<span style="-ms-word-wrap: break-word;">__rmqueue_fallback()</span>进行分配。</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">接下来看一下<span style="-ms-word-wrap: break-word;">__rmqueue_fallback()</span>实现:</span>


1. 【file:/mm/page_alloc.c】
2. / Remove an element from the buddy allocator from the fallback list /
3. static inline struct page
4. __rmqueue_fallback(struct zone zone, int order, int start_migratetype)
5. {
6.     struct free_area area;
7.     int current_order;
8.     struct page page;
9.     int migratetype, new_type, i;
10.  
11.     / Find the largest possible block of pages in the other list /
12.     for (current_order = MAX_ORDER-1; current_order >= order;
13.                         --current_order) {
14.         for (i = 0;; i++) {
15.             migratetype = fallbacks[start_migratetype][i];
16.  
17.             / MIGRATE_RESERVE handled later if necessary /
18.             if (migratetype == MIGRATE_RESERVE)
19.                 break;
20.  
21.             area = &(zone->free_area[current_order]);
22.             if (list_empty(&area->free_list[migratetype]))
23.                 continue;
24.  
25.             page = list_entry(area->free_list[migratetype].next,
26.                     struct page, lru);
27.             area->nr_free--;
28.  
29.             new_type = try_to_steal_freepages(zone, page,
30.                               start_migratetype,
31.                               migratetype);
32.  
33.             / Remove the page from the freelists /
34.             list_del(&page->lru);
35.             rmv_page_order(page);
36.  
37.             expand(zone, page, order, current_order, area,
38.                    new_type);
39.  
40.             trace_mm_page_alloc_extfrag(page, order, current_order,
41.                 start_migratetype, migratetype, new_type);
42.  
43.             return page;
44.         }
45.     }
46.  
47.     return NULL;
48. }
<span style="font-family: 宋体; -ms-word-wrap: break-word;">可以看到其异于通常的伙伴管理算法,内存页面是由最高阶开始进行查找的,而查找的迁移类型是根据<span style="-ms-word-wrap: break-word;">fallbacks</span>备选类型中进行遍历获得并止于<span style="-ms-word-wrap: break-word;">MIGRATE_RESERVE</span>类型。由此获得的阶号和迁移类型查找<span style="-ms-word-wrap: break-word;">zone-&gt;free_area[]->free_list[]</span>空闲页面管理链表,如果查找到的话,则将其摘除,否则进入下一类型查找,最后所有类型都查找不到的时候,才会降阶查找。</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">其中<span style="-ms-word-wrap: break-word;">fallbacks[][]</span>是已确定的类型顺序结构,其定义为:</span>


1. 【file:/mm/page_alloc.c】
2. /
3.   This array describes the order lists are fallen back to when
4.   the free lists for the desirable migrate type are depleted
5.  /
6. static int fallbacks[MIGRATE_TYPES][4] = {
7.     [MIGRATE_UNMOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },
8.     [MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },
9. #ifdef CONFIG_CMA
10.     [MIGRATE_MOVABLE] = { MIGRATE_CMA, MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },
11.     [MIGRATE_CMA] = { MIGRATE_RESERVE }, / Never used /
12. #else
13.     [MIGRATE_MOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },
14. #endif
15.     [MIGRATE_RESERVE] = { MIGRATE_RESERVE }, / Never used /
16. #ifdef CONFIG_MEMORY_ISOLATION
17.     [MIGRATE_ISOLATE] = { MIGRATE_RESERVE }, / Never used /
18. #endif
19. };
<span style="font-family: 宋体; -ms-word-wrap: break-word;">具体分析一下<span style="-ms-word-wrap: break-word;">try_to_steal_freepages()</span>函数实现:</span>


1. 【file:/mm/page_alloc.c】
2. /
3.   If breaking a large block of pages, move all free pages to the preferred
4.   allocation list. If falling back for a reclaimable kernel allocation, be
5.   more aggressive about taking ownership of free pages.
6.  
7.   On the other hand, never change migration type of MIGRATE_CMA pageblocks
8.   nor move CMA pages to different free lists. We don't want unmovable pages
9.   to be allocated from MIGRATE_CMA areas.
10.  
11.   Returns the new migratetype of the pageblock (or the same old migratetype
12.   if it was unchanged).
13.  /
14. static int try_to_steal_freepages(struct zone zone, struct page page,
15.                   int start_type, int fallback_type)
16. {
17.     int current_order = page_order(page);
18.  
19.     /
20.       When borrowing from MIGRATE_CMA, we need to release the excess
21.       buddy pages to CMA itself.
22.      /
23.     if (is_migrate_cma(fallback_type))
24.         return fallback_type;
25.  
26.     / Take ownership for orders >= pageblock_order /
27.     if (current_order >= pageblock_order) {
28.         change_pageblock_range(page, current_order, start_type);
29.         return start_type;
30.     }
31.  
32.     if (current_order >= pageblock_order / 2 ||
33.         start_type == MIGRATE_RECLAIMABLE ||
34.         page_group_by_mobility_disabled) {
35.         int pages;
36.  
37.         pages = move_freepages_block(zone, page, start_type);
38.  
39.         / Claim the whole block if over half of it is free /
40.         if (pages >= (1 << (pageblock_order-1)) ||
41.                 page_group_by_mobility_disabled) {
42.  
43.             set_pageblock_migratetype(page, start_type);
44.             return start_type;
45.         }
46.  
47.     }
48.  
49.     return fallback_type;
50. }
<span style="font-family: 宋体; -ms-word-wrap: break-word;">该函数主要实现了内存页面的迁移类型的变更,将<span style="-ms-word-wrap: break-word;">__rmqueue_fallback()</span>查找到满足需要的内存页面空间类型转为申请的类型。其中<span style="-ms-word-wrap: break-word;">MIGRATE_CMA</span>类型不做类型转换,此外类型转换的页面数量为<span style="-ms-word-wrap: break-word;">pageblock_nr_pages</span>为单位的倍数,还有就是对于阶较低的内存页面(小于<span style="-ms-word-wrap: break-word;">pageblock_order/2</span>)、类型不为<span style="-ms-word-wrap: break-word;">MIGRATE_RECLAIMABLE</span>且未开启页面迁移的情况下,是不做类型转换的。完了,在<span style="-ms-word-wrap: break-word;">__rmqueue_fallback()</span>里面根据其转换后的类型通过<span style="-ms-word-wrap: break-word;">expand()</span>扩展到对应的迁移类型伙伴管理系统中。</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">小结一下,<span style="-ms-word-wrap: break-word;">__rmqueue_fallback()</span>是自高往低阶遍历<span style="-ms-word-wrap: break-word;">fallbacks</span>迁移类型表,查找满足分配需要的内存页面,然后将查找到的内存页面进行类型变更后合并到所申请的类型中,以实现类型迁移。值得注意的是,之所以内存迁移都是以内存块的高阶进行的,主要就是为了反碎片化,避免当前类型无法满足需要的时候,过于频繁地向备选类型进行小片内存申请和做迁移而导致备选类型的内存页面产生大量水平,将问题控制在所申请的内存类型中。</span>

<span style="font-family: 宋体; -ms-word-wrap: break-word;">最后看一下<span style="-ms-word-wrap: break-word;">set_pageblock_migratetype()</span>的实现:</span>


1. 【file:/mm/page_alloc.c】
2. /
3.   set_pageblock_flags_mask - Set the requested group of flags for a pageblock_nr_pages block of pages
4.   @page: The page within the block of interest
5.   @start_bitidx: The first bit of interest
6.   @end_bitidx: The last bit of interest
7.   @flags: The flags to set
8.  /
9. void set_pageblock_flags_mask(struct page page, unsigned long flags,
10.                     unsigned long end_bitidx,
11.                     unsigned long mask)
12. {
13.     struct zone zone;
14.     unsigned long *bitmap;
15.     unsigned long pfn, bitidx, word_bitidx;
16.     unsigned long old_word, word;
17.  
18.     BUILD_BUG_ON(NR_PAGEBLOCK_BITS != 4);
19.  
20.     zone = page_zone(page);
21.     pfn = page_to_pfn(page);
22.     bitmap = get_pageblock_bitmap(zone, pfn);
23.     bitidx = pfn_to_bitidx(zone, pfn);
24.     word_bitidx = bitidx / BITS_PER_LONG;
25.     bitidx &= (BITS_PER_LONG-1);
26.  
27.     VM_BUG_ON_PAGE(!zone_spans_pfn(zone, pfn), page);
28.  
29.     bitidx += end_bitidx;
30.     mask <<= (BITS_PER_LONG - bitidx - 1);
31.     flags <<= (BITS_PER_LONG - bitidx - 1);
32.  
33.     word = ACCESS_ONCE(bitmap[word_bitidx]);
34.     for (;;) {
35.         old_word = cmpxchg(&bitmap[word_bitidx], word, (word & ~mask) | flags);
36.         if (word == old_word)
37.             break;
38.         word = old_word;
39.     }
40. }
<span style="font-family: 宋体; -ms-word-wrap: break-word;">其中<span style="-ms-word-wrap: break-word;">get_pageblock_bitmap()</span>用于取得<span style="-ms-word-wrap: break-word;">zone</span>结构体中<span style="-ms-word-wrap: break-word;">pageblock_flags</span>成员,而后面则是基于此做位图操作。通过该函数,可以看到内存页面的类型管理是通过其所属的<span style="-ms-word-wrap: break-word;">zone</span>的结构体中的<span style="-ms-word-wrap: break-word;">pageblock_flags</span>所管理的位图进行标识该页面的迁移类型。</span>

[http://blog.chinaunix.net/uid-26859697-id-4939357.html](http://blog.chinaunix.net/uid-26859697-id-4939357.html)

<span style="font-family: 宋体; -ms-word-wrap: break-word;">&nbsp;</span>