您的位置:首页 > 运维架构 > Linux

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

2011-11-28 17:56 483 查看
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吧。最新的内核版本中已经没有这个参数了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐