http://blog.chinaunix.net/uid-26859697-id-4732565.html

此处接前文,分析<span style="-ms-word-wrap: break-word;">free_area_init_nodes()</span>函数最后部分,分析其末尾的循环:

&nbsp;&nbsp;&nbsp; for_each_online_node(nid) {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pg_data_t *pgdat = NODE_DATA(nid);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; free_area_init_node(nid, NULL,

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; find_min_pfn_for_node(nid), NULL);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Any memory on that node */

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (pgdat-&gt;node_present_pages)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; node_set_state(nid, N_MEMORY);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; check_for_memory(pgdat, nid);

&nbsp;&nbsp;&nbsp; }

这里面的关键函数是<span style="-ms-word-wrap: break-word;">free_area_init_node()</span>,其入参<span style="-ms-word-wrap: break-word;">find_min_pfn_for_node()</span>用于获取<span style="-ms-word-wrap: break-word;">node</span>节点中最低的内存页框号。

而<span style="-ms-word-wrap: break-word;">free_area_init_node()</span>其实现:


1. 【file:/mm/page_alloc.c】
2. void __paginginit free_area_init_node(int nid, unsigned long zones_size,
3.         unsigned long node_start_pfn, unsigned long zholes_size)
4. {
5.     pg_data_t pgdat = NODE_DATA(nid);
6.     unsigned long start_pfn = 0;
7.     unsigned long end_pfn = 0;
8.  
9.     / pg_data_t should be reset to zero when it's allocated */
10.     WARN_ON(pgdat->nr_zones || pgdat->classzone_idx);
11.  
12.     pgdat->node_id = nid;
13.     pgdat->node_start_pfn = node_start_pfn;
14.     init_zone_allows_reclaim(nid);
15. #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
16.     get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
17. #endif
18.     calculate_node_totalpages(pgdat, start_pfn, end_pfn,
19.                   zones_size, zholes_size);
20.  
21.     alloc_node_mem_map(pgdat);
22. #ifdef CONFIG_FLAT_NODE_MEM_MAP
23.     printk(KERN_DEBUG "free_area_init_node: node %d, pgdat %08lx, node_mem_map %08lx\n",
24.         nid, (unsigned long)pgdat,
25.         (unsigned long)pgdat->node_mem_map);
26. #endif
27.  
28.     free_area_init_core(pgdat, start_pfn, end_pfn,
29.                 zones_size, zholes_size);
30. }
&nbsp;

该函数中,其中<span style="-ms-word-wrap: break-word;">init_zone_allows_reclaim()</span>用于计算评估内存管理区是否可回收以及合适的<span style="-ms-word-wrap: break-word;">node</span>节点数,如果非<span style="-ms-word-wrap: break-word;">NUMA</span>环境,则该函数为空。而基于<span style="-ms-word-wrap: break-word;">CONFIG_HAVE_MEMBLOCK_NODE_MAP</span>的配置下,接下来将是<span style="-ms-word-wrap: break-word;">get_pfn_range_for_nid()</span>:


1. 【file:/mm/page_alloc.c】
2. /
3.   get_pfn_range_for_nid - Return the start and end page frames for a node
4.   @nid: The nid to return the range for. If MAX_NUMNODES, the min and max PFN are returned.
5.   @start_pfn: Passed by reference. On return, it will have the node start_pfn.
6.   @end_pfn: Passed by reference. On return, it will have the node end_pfn.
7.  
8.   It returns the start and end page frame of a node based on information
9.   provided by an arch calling add_active_range(). If called for a node
10.   with no available memory, a warning is printed and the start and end
11.   PFNs will be 0.
12.  /
13. void __meminit get_pfn_range_for_nid(unsigned int nid,
14.             unsigned long start_pfn, unsigned long end_pfn)
15. {
16.     unsigned long this_start_pfn, this_end_pfn;
17.     int i;
18.  
19.     start_pfn = -1UL;
20.     end_pfn = 0;
21.  
22.     for_each_mem_pfn_range(i, nid, &this_start_pfn, &this_end_pfn, NULL) {
23.         start_pfn = min(start_pfn, this_start_pfn);
24.         end_pfn = max(end_pfn, this_end_pfn);
25.     }
26.  
27.     if (start_pfn == -1UL)
28.         start_pfn = 0;
29. }
&nbsp;

此函数主要是将内存<span style="-ms-word-wrap: break-word;">node</span>节点的起始和末尾页框号返回给接下来的<span style="-ms-word-wrap: break-word;">calculate_node_totalpages()</span>来使用。

calculate_node_totalpages()实现:


1. 【file:/mm/page_alloc.c】
2. static void __meminit calculate_node_totalpages(struct pglist_data pgdat,
3.                         unsigned long node_start_pfn,
4.                         unsigned long node_end_pfn,
5.                         unsigned long zones_size,
6.                         unsigned long *zholes_size)
7. {
8.     unsigned long realtotalpages, totalpages = 0;
9.     enum zone_type i;
10.  
11.     for (i = 0; i < MAX_NR_ZONES; i++)
12.         totalpages += zone_spanned_pages_in_node(pgdat->node_id, i,
13.                              node_start_pfn,
14.                              node_end_pfn,
15.                              zones_size);
16.     pgdat->node_spanned_pages = totalpages;
17.  
18.     realtotalpages = totalpages;
19.     for (i = 0; i < MAX_NR_ZONES; i++)
20.         realtotalpages -=
21.             zone_absent_pages_in_node(pgdat->node_id, i,
22.                           node_start_pfn, node_end_pfn,
23.                           zholes_size);
24.     pgdat->node_present_pages = realtotalpages;
25.     printk(KERN_DEBUG "On node %d totalpages: %lu\n", pgdat->node_id,
26.                             realtotalpages);
27. }
&nbsp;

其中<span style="-ms-word-wrap: break-word;">zone_spanned_pages_in_node()</span>:


1. 【file:/mm/page_alloc.c】
2. /
3.   Return the number of pages a zone spans in a node, including holes
4.   present_pages = zone_spanned_pages_in_node() - zone_absent_pages_in_node()
5.  /
6. static unsigned long __meminit zone_spanned_pages_in_node(int nid,
7.                     unsigned long zone_type,
8.                     unsigned long node_start_pfn,
9.                     unsigned long node_end_pfn,
10.                     unsigned long ignored)
11. {
12.     unsigned long zone_start_pfn, zone_end_pfn;
13.  
14.     / Get the start and end of the zone /
15.     zone_start_pfn = arch_zone_lowest_possible_pfn[zone_type];
16.     zone_end_pfn = arch_zone_highest_possible_pfn[zone_type];
17.     adjust_zone_range_for_zone_movable(nid, zone_type,
18.                 node_start_pfn, node_end_pfn,
19.                 &zone_start_pfn, &zone_end_pfn);
20.  
21.     / Check that this node has pages within the zone's required range /
22.     if (zone_end_pfn < node_start_pfn || zone_start_pfn > node_end_pfn)
23.         return 0;
24.  
25.     / Move the zone boundaries inside the node if necessary /
26.     zone_end_pfn = min(zone_end_pfn, node_end_pfn);
27.     zone_start_pfn = max(zone_start_pfn, node_start_pfn);
28.  
29.     / Return the spanned pages */
30.     return zone_end_pfn - zone_start_pfn;
31. }
&nbsp;

其主要是统计<span style="-ms-word-wrap: break-word;">node</span>管理节点的内存跨度,该跨度不包括<span style="-ms-word-wrap: break-word;">movable</span>管理区的,里面调用的<span style="-ms-word-wrap: break-word;">adjust_zone_range_for_zone_movable()</span>则是用于剔除<span style="-ms-word-wrap: break-word;">movable</span>管理区的部分。

另外的<span style="-ms-word-wrap: break-word;">zone_absent_pages_in_node()</span>:


1. 【file:/mm/page_alloc.c】
2. / Return the number of page frames in holes in a zone on a node /
3. static unsigned long meminit zone_absent_pages_in_node(int nid,
4.                     unsigned long zone_type,
5.                     unsigned long node_start_pfn,
6.                     unsigned long node_end_pfn,
7.                     unsigned long *ignored)
8. {
9.     unsigned long zone_low = arch_zone_lowest_possible_pfn[zone_type];
10.     unsigned long zone_high = arch_zone_highest_possible_pfn[zone_type];
11.     unsigned long zone_start_pfn, zone_end_pfn;
12.  
13.     zone_start_pfn = clamp(node_start_pfn, zone_low, zone_high);
14.     zone_end_pfn = clamp(node_end_pfn, zone_low, zone_high);
15.  
16.     adjust_zone_range_for_zone_movable(nid, zone_type,
17.             node_start_pfn, node_end_pfn,
18.             &zone_start_pfn, &zone_end_pfn);
19.     return
absent_pages_in_range(nid, zone_start_pfn, zone_end_pfn);
20. }
&nbsp;

该函数主要用于计算内存空洞页面数的。完了将会得到物理页面总数并在<span style="-ms-word-wrap: break-word;">calculate_node_totalpages()</span>中将页面总数打印出来:

&nbsp;

紧接着在<span style="-ms-word-wrap: break-word;">free_area_init_node()</span>调用的是<span style="-ms-word-wrap: break-word;">alloc_node_mem_map()</span>:


1. 【file:/mm/page_alloc.c】
2. static void __init_refok alloc_node_mem_map(struct pglist_data pgdat)
3. {
4.     / Skip empty nodes /
5.     if (!pgdat->node_spanned_pages)
6.         return;
7.  
8. #ifdef CONFIG_FLAT_NODE_MEM_MAP
9.     / ia64 gets its own node_mem_map, before this, without bootmem /
10.     if (!pgdat->node_mem_map) {
11.         unsigned long size, start, end;
12.         struct page map;
13.  
14.         /
15.           The zone's endpoints aren't required to be MAX_ORDER
16.           aligned but the node_mem_map endpoints must be in order
17.           for the buddy allocator to function correctly.
18.          /
19.         start = pgdat->node_start_pfn & ~(MAX_ORDER_NR_PAGES - 1);
20.         end = pgdat_end_pfn(pgdat);
21.         end = ALIGN(end, MAX_ORDER_NR_PAGES);
22.         size = (end - start) sizeof(struct page);
23.         map = alloc_remap(pgdat->node_id, size);
24.         if (!map)
25.             map = memblock_virt_alloc_node_nopanic(size,
26.                                    pgdat->node_id);
27.         pgdat->node_mem_map = map + (pgdat->node_start_pfn - start);
28.     }
29. #ifndef CONFIG_NEED_MULTIPLE_NODES
30.     /
31.       With no DISCONTIG, the global mem_map is just set as node 0's
32.      /
33.     if (pgdat == NODE_DATA(0)) {
34.         mem_map = NODE_DATA(0)->node_mem_map;
35. #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
36.         if (page_to_pfn(mem_map) != pgdat->node_start_pfn)
37.             mem_map -= (pgdat->node_start_pfn - ARCH_PFN_OFFSET);
38. #endif / CONFIG_HAVE_MEMBLOCK_NODE_MAP /
39.     }
40. #endif
41. #endif / CONFIG_FLAT_NODE_MEM_MAP */
42. }
&nbsp;

其主要将<span style="-ms-word-wrap: break-word;">calculate_node_totalpages()</span>统计所得的内存页面信息进行内存空间申请。

得到内存空间后,初始化工作将交由<span style="-ms-word-wrap: break-word;">free_area_init_core()</span>:


1. 【file:/mm/page_alloc.c】
2. /
3.   Set up the zone data structures:
4.   - mark all pages reserved
5.   - mark all memory queues empty
6.   - clear the memory bitmaps
7.  
8.   NOTE: pgdat should get zeroed by caller.
9.  /
10. static void __paginginit free_area_init_core(struct pglist_data pgdat,
11.         unsigned long node_start_pfn, unsigned long node_end_pfn,
12.         unsigned long zones_size, unsigned long zholes_size)
13. {
14.     enum zone_type j;
15.     int nid = pgdat->node_id;
16.     unsigned long zone_start_pfn = pgdat->node_start_pfn;
17.     int ret;
18.  
19.     pgdat_resize_init(pgdat);
20. #ifdef CONFIG_NUMA_BALANCING
21.     spin_lock_init(&pgdat->numabalancing_migrate_lock);
22.     pgdat->numabalancing_migrate_nr_pages = 0;
23.     pgdat->numabalancing_migrate_next_window = jiffies;
24. #endif
25.     init_waitqueue_head(&pgdat->kswapd_wait);
26.     init_waitqueue_head(&pgdat->pfmemalloc_wait);
27.     pgdat_page_cgroup_init(pgdat);
28.  
29.     for (j = 0; j < MAX_NR_ZONES; j++) {
30.         struct zone zone = pgdat->node_zones + j;
31.         unsigned long size, realsize, freesize, memmap_pages;
32.  
33.         size = zone_spanned_pages_in_node(nid, j, node_start_pfn,
34.                           node_end_pfn, zones_size);
35.         realsize = freesize = size - zone_absent_pages_in_node(nid, j,
36.                                 node_start_pfn,
37.                                 node_end_pfn,
38.                                 zholes_size);
39.  
40.         /
41.           Adjust freesize so that it accounts for how much memory
42.           is used by this zone for memmap. This affects the watermark
43.           and per-cpu initialisations
44.          /
45.         memmap_pages = calc_memmap_size(size, realsize);
46.         if (freesize >= memmap_pages) {
47.             freesize -= memmap_pages;
48.             if (memmap_pages)
49.                 printk(KERN_DEBUG
50.                        " %s zone: %lu pages used for memmap\n",
51.                        zone_names[j], memmap_pages);
52.         } else
53.             printk(KERN_WARNING
54.                 " %s zone: %lu pages exceeds freesize %lu\n",
55.                 zone_names[j], memmap_pages, freesize);
56.  
57.         / Account for reserved pages /
58.         if (j == 0 && freesize > dma_reserve) {
59.             freesize -= dma_reserve;
60.             printk(KERN_DEBUG " %s zone: %lu pages reserved\n",
61.                     zone_names[0], dma_reserve);
62.         }
63.  
64.         if (!is_highmem_idx(j))
65.             nr_kernel_pages += freesize;
66.         / Charge for highmem memmap if there are enough kernel pages /
67.         else if (nr_kernel_pages > memmap_pages 2)
68.             nr_kernel_pages -= memmap_pages;
69.         nr_all_pages += freesize;
70.  
71.         zone->spanned_pages = size;
72.         zone->present_pages = realsize;
73.         /
74.           Set an approximate value for lowmem here, it will be adjusted
75.           when the bootmem allocator frees pages into the buddy system.
76.           And all highmem pages will be managed by the buddy system.
77.          /
78.         zone->managed_pages = is_highmem_idx(j) ? realsize : freesize;
79. #ifdef CONFIG_NUMA
80.         zone->node = nid;
81.         zone->min_unmapped_pages = (freesizesysctl_min_unmapped_ratio)
82.                         / 100;
83.         zone->min_slab_pages = (freesize sysctl_min_slab_ratio) / 100;
84. #endif
85.         zone->name = zone_names[j];
86.         spin_lock_init(&zone->lock);
87.         spin_lock_init(&zone->lru_lock);
88.         zone_seqlock_init(zone);
89.         zone->zone_pgdat = pgdat;
90.         zone_pcp_init(zone);
91.  
92.         / For bootup, initialized properly in watermark setup */
93.         mod_zone_page_state(zone, NR_ALLOC_BATCH, zone->managed_pages);
94.  
95.         lruvec_init(&zone->lruvec);
96.         if (!size)
97.             continue;
98.  
99.         set_pageblock_order();
100.         setup_usemap(pgdat, zone, zone_start_pfn, size);
101.         ret = init_currently_empty_zone(zone, zone_start_pfn,
102.                         size, MEMMAP_EARLY);
103.         BUG_ON(ret);
104.         memmap_init(size, nid, j, zone_start_pfn);
105.         zone_start_pfn += size;
106.     }
107. }
<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: 宋体, Arial; 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;">&nbsp; &nbsp; &nbsp;&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: 宋体, Arial; 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;">pgdat_resize_init()</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: 宋体, Arial; 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: 宋体, Arial; 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;">init_waitqueue_head()</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: 宋体, Arial; 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: 宋体, Arial; 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;">pgdat_page_cgroup_init()</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: 宋体, Arial; 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>

&nbsp;

而在<span style="-ms-word-wrap: break-word;">for</span>循环内,循环遍历统计各个管理区最大跨度间相差的页面数<span style="-ms-word-wrap: break-word;">size</span>以及除去内存&ldquo;空洞&rdquo;后的实际页面数<span style="-ms-word-wrap: break-word;">realsize,</span>然后通过<span style="-ms-word-wrap: break-word;">calc_memmap_size()</span>计算出该管理区所需的页面管理结构占用的页面数<span style="-ms-word-wrap: break-word;">memmap_pages</span>,最后可以计算得除高端内存外的系统内存共有的内存页面数<span style="-ms-word-wrap: break-word;">nr_kernel_pages</span>(用于统计所有一致映射的页);此外循环体内的操作则是初始化内存管理区的管理结构,例如各类锁的初始化、队列初始化。值得注意的是<span style="-ms-word-wrap: break-word;">zone_pcp_init()</span>是初始化冷热页分配器的,<span style="-ms-word-wrap: break-word;">mod_zone_page_state()</span>用于计算更新管理区的状态统计,<span style="-ms-word-wrap: break-word;">lruvec_init()</span>则是初始化<span style="-ms-word-wrap: break-word;">LRU</span>算法使用的链表和保护锁,而<span style="-ms-word-wrap: break-word;">set_pageblock_order()</span>用于在<span style="-ms-word-wrap: break-word;">CONFIG_HUGETLB_PAGE_SIZE_VARIABLE</span>配置下设置<span style="-ms-word-wrap: break-word;">pageblock_order</span>值的;此外<span style="-ms-word-wrap: break-word;">setup_usemap()</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;">pageblock_flags</span>与伙伴系统的碎片迁移算法有关。而<span style="-ms-word-wrap: break-word;">init_currently_empty_zone()</span>则主要初始化管理区的等待队列哈希表和等待队列,同时还初始化了与伙伴系统相关的<span style="-ms-word-wrap: break-word;">free_aera</span>列表。

中间有部分日志记录可以通过<span style="-ms-word-wrap: break-word;">dmesg</span>查看到:

&nbsp;

在<span style="-ms-word-wrap: break-word;">free_area_init_core()</span>的最后,着重分析一下<span style="-ms-word-wrap: break-word;">memmap_init()</span>:


1. 【file:/mm/page_alloc.c】
2. #define memmap_init(size, nid, zone, start_pfn) \
3.     memmap_init_zone((size), (nid), (zone), (start_pfn), MEMMAP_EARLY)
&nbsp;

其对应的是<span style="-ms-word-wrap: break-word;">memmap_init_zone()</span>:


1. 【file:/mm/page_alloc.c】
2. /
3.   Initially all pages are reserved - free ones are freed
4.   up by free_all_bootmem() once the early boot process is
5.   done. Non-atomic initialization, single-pass.
6.  /
7. void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
8.         unsigned long start_pfn, enum memmap_context context)
9. {
10.     struct page page;
11.     unsigned long end_pfn = start_pfn + size;
12.     unsigned long pfn;
13.     struct zone z;
14.  
15.     if (highest_memmap_pfn < end_pfn - 1)
16.         highest_memmap_pfn = end_pfn - 1;
17.  
18.     z = &NODE_DATA(nid)->node_zones[zone];
19.     for (pfn = start_pfn; pfn < end_pfn; pfn++) {
20.         /
21.           There can be holes in boot-time mem_map[]s
22.           handed to this function. They do not
23.           exist on hotplugged memory.
24.          /
25.         if (context == MEMMAP_EARLY) {
26.             if (!early_pfn_valid(pfn))
27.                 continue;
28.             if (!early_pfn_in_nid(pfn, nid))
29.                 continue;
30.         }
31.         page = pfn_to_page(pfn);
32.         set_page_links(page, zone, nid, pfn);
33.         mminit_verify_page_links(page, zone, nid, pfn);
34.         init_page_count(page);
35.         page_mapcount_reset(page);
36.         page_cpupid_reset_last(page);
37.         SetPageReserved(page);
38.         /
39.           Mark the block movable so that blocks are reserved for
40.           movable at startup. This will force kernel allocations
41.           to reserve their blocks rather than leaking throughout
42.           the address space during boot when many long-lived
43.           kernel allocations are made. Later some blocks near
44.           the start are marked MIGRATE_RESERVE by
45.           setup_zone_migrate_reserve()
46.          
47.           bitmap is created for zone's valid pfn range. but memmap
48.           can be created for invalid pages (for alignment)
49.           check here not to call set_pageblock_migratetype() against
50.           pfn out of zone.
51.          /
52.         if ((z->zone_start_pfn <= pfn)
53.             && (pfn < zone_end_pfn(z))
54.             && !(pfn & (pageblock_nr_pages - 1)))
55.             set_pageblock_migratetype(page, MIGRATE_MOVABLE);
56.  
57.         INIT_LIST_HEAD(&page->lru);
58. #ifdef WANT_PAGE_VIRTUAL
59.         / The shift won't overflow because ZONE_NORMAL is below 4G. /
60.         if (!is_highmem_idx(zone))
61.             set_page_address(page, __va(pfn << PAGE_SHIFT));
62. #endif
63.     }
64. }
&nbsp;

该函数主要根据页框号<span style="-ms-word-wrap: break-word;">pfn</span>通过<span style="-ms-word-wrap: break-word;">pfn_to_page()</span>查找到页面管理结构<span style="-ms-word-wrap: break-word;">page</span>,而后面的操作则是对该页面的管理结构<span style="-ms-word-wrap: break-word;">page</span>进行初始化。

至此,<span style="-ms-word-wrap: break-word;">free_area_init_node()</span>的初始化操作执行完毕,据前面分析可以知道其主要是将整个<span style="-ms-word-wrap: break-word;">linux</span>物理内存管理框架进行初始化,包括管理节点<span style="-ms-word-wrap: break-word;">node</span>、管理区<span style="-ms-word-wrap: break-word;">zone</span>以及页面管理<span style="-ms-word-wrap: break-word;">page</span>等数据的初始化。

回到本文主题,循环体内最后的两个函数<span style="-ms-word-wrap: break-word;">node_set_state()</span>和<span style="-ms-word-wrap: break-word;">check_for_memory()</span>。<span style="-ms-word-wrap: break-word;">node_set_state()</span>主要是对<span style="-ms-word-wrap: break-word;">node</span>节点进行状态设置,而<span style="-ms-word-wrap: break-word;">check_for_memory()</span>则是做内存检查。

至此,内存管理框架构建完毕。