Linux内核初始化高端内存的过程(代码分析)

Linux内核初始化高端内存的过程

 

         内核在start_kernel()函数中调用了mem_init()来做所有与内存初始化相关的工作。与初始化高端内存相关的工作在函数set_highmem_pages_init()中完成。下面我们来详细分析一下这个过程。

109 void __init set_highmem_pages_init(void)

110 {

111         struct zone *zone;

112         int nid;        

113

114         for_each_zone(zone) {

115                 unsigned long zone_start_pfn, zone_end_pfn;

116

117                 if (!is_highmem(zone))

118                         continue;

119

120                 zone_start_pfn = zone->zone_start_pfn;

121                 zone_end_pfn = zone_start_pfn + zone->spanned_pages;

122

123                 nid = zone_to_nid(zone);

124                 printk(KERN_INFO "Initializing %s for node %d(%08lx:%08lx)\n",

125                                 zone->name, nid, zone_start_pfn, zone_end_pfn);

126

127                 add_highpages_with_active_regions(nid, zone_start_pfn,

128                                  zone_end_pfn);

129         }      

130         totalram_pages += totalhigh_pages;

131 } 

         这个函数首先通过for_each_zone(zone)遍历所有的zone,以找到高端内存管理器。117行的is_highmem(zone)判定zone是否是高端内存。如果是则继续执行120行之后的代码,否则继续判断下一个zone。变量zone_start_pfn与zone_end_pfn表示高端管理区的首位pfn。123行的zone_to_nid(zone)返回zone所在的节点号,即nid。124行输出高端内存相关的信息。在130行,将初始化后的高端内存页面计入到全局变量totalram_pages中。totalram_pages表示系统中所有的物理页面数。函数add_highpages_with_active_regions()将完成进一步的工作。

455 void __init add_highpages_with_active_regions(int nid, unsigned long start_pfn,

 456                                               unsigned long end_pfn)

 457 {

 458         struct add_highpages_data data;

 459       

 460         data.start_pfn = start_pfn;

 461         data.end_pfn = end_pfn;

 462       

 463         work_with_active_regions(nid, add_highpages_work_fn, &data);

 464 }

         这个函数将高端管理区的始末pfn封装在结构add_highpages_data中,然后调用函数work_with_active_regions完成进一步的工作。这里提出了一个active range的概念,可以将其译为活动内存区。active range是为了描述地址空间中的可用区域,排除了地址空间中的空洞。active range存储在数组early_node_map[MAX_ACTIVE_REGIONS]中。该数组中的每个元素保存了该active range的始末pfn,以及所属节点的节点号。函数work_with_active_regions(nid, add_highpages_work_fn, &data)节点nid中的每个active range均调用一次函数add_highpages_work_fn(),目的是排除高端管理区中的空洞(hole),即那些不可使用的页面。work_with_active_regions的实现如下:

3427 void __init work_with_active_regions(int nid, work_fn_t work_fn, void *data)

3428 {

3429         int i;

3430         int ret;

3431

3432         for_each_active_range_index_in_nid(i, nid) {

3433                 ret = work_fn(early_node_map[i].start_pfn,

3434                               early_node_map[i].end_pfn, data);

3435                 if (ret)

3436                         break;

3437         }

3438 }

         宏for_each_active_range_index_in_nid即用来遍历节点nid中的所有active range。early_node_map[i].start_pfn与early_node_map[i].end_pfn表示了当前active range的始末pfn。

         函数add_highpages_work_fn()则对高端内存落在当前active range中的页面进行初始化,其实现如下:

428 static int __init add_highpages_work_fn(unsigned long start_pfn,

 429                                          unsigned long end_pfn, void *datax)

 430 {

 431         int node_pfn;

 432         struct page *page;

 433         unsigned long final_start_pfn, final_end_pfn;

 434         struct add_highpages_data *data;

 435

 436         data = (struct add_highpages_data *)datax;

 437

 438         final_start_pfn = max(start_pfn, data->start_pfn);

 439         final_end_pfn = min(end_pfn, data->end_pfn);

 440         if (final_start_pfn >= final_end_pfn)

 441                return 0;

 442                

 443         for (node_pfn = final_start_pfn; node_pfn < final_end_pfn;

 444             node_pfn++) {

 445                if (!pfn_valid(node_pfn))

 446                         continue;

 447                page = pfn_to_page(node_pfn);

 448                add_one_highpage_init(page, node_pfn);

 449         }

 450                

 451         return 0;

 452                                

 453 }

         438与439行求出了当前active range与高端管理区(由data表示)的交集。443行开始的for循环,遍历了这个交集中的每个页面。在验证该页面的有效性之后,调用add_one_highpage_init()函数将该页面释放到伙伴系统中。add_one_highpage_init()函数的实现很简单:

415 static void __init add_one_highpage_init(struct page *page, int pfn)

 416 {

 417         ClearPageReserved(page);

 418         init_page_count(page);

 419         __free_page(page);

 420         totalhigh_pages++;

 421 }

         ClearPageReserved清除了该页面flag中的reserved标志,表示该页面属于动态内存。init_page_count(page)将该页面的引用计数初始化为1。__free_page(page)将该页面真正释放到伙伴系统中。该函数的第二个参数node_pfn实际上并没有被用到,这算是该版本内核(2.6.32.2)的一个Bug吧。最新的内核版本中已经没有这个参数了。

 

 

你可能感兴趣的:(工作,struct,活动,each,代码分析,linux内核)