zone初始化调用:zone_sizes_init->free_area_init_nodes
free_area_init_nodes()的参数max_zone_pfns是一个数组,pfn(page frame num),其实就是标识了各个内存域的起始页帧(页框)标号。ZONE_DMA之前还有大约1M的内存是留给bootloader的。通过这个也可以计算出各个内存域的大小。
MAX_DMA_PFN = 16Mb
MAX_DMA32_PFN = 4Gb
void __init zone_sizes_init(void)
{
unsigned long max_zone_pfns[MAX_NR_ZONES];
memset(max_zone_pfns, 0, sizeof(max_zone_pfns));
#ifdef CONFIG_ZONE_DMA
max_zone_pfns[ZONE_DMA] = min(MAX_DMA_PFN, max_low_pfn);
#endif
#ifdef CONFIG_ZONE_DMA32
max_zone_pfns[ZONE_DMA32] = min(MAX_DMA32_PFN, max_low_pfn);
#endif
max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
#ifdef CONFIG_HIGHMEM
max_zone_pfns[ZONE_HIGHMEM] = max_pfn;
#endif
free_area_init_nodes(max_zone_pfns);
}
从这里可以看出,DMA32最大的范围也就是到4G了,如果物理内存大于4G,可能为4G,这要看e820探测的内存
有没有大于4G的usable的内存。DMA最大也就是到16M了。
max_low_pfn看看如何赋值?
由于这个是64位设备,这就需要确定max_pfn如何赋值?
MAX_ARCH_PFN 计算出来是16G个页帧,也就是64TB内存大小。
e820_end_pfn函数的作用是在不超过limit_pfn的最大可可用帧号
这里的打因信息为:
e820: last_pfn = 0x880000 max_arch_pfn = 0x400000000
结合之前打印e820的信息:
计算最后一个usable区域的换算成页帧为0x880000,从这里也可以看出,max_pfn就是e820探测的实际物理内存映射到物理内存地址的最大页帧号。
继续计算,x86-64cpu的PAGE_SHIGT=12,那么max_pfn > 1UL << 20,所以
max_low_pfn = e820_end_of_low_ram_pfn()
可计算出max_low_pfn = 0x7bb00。
综上,可以看看最后的zone的范围打印信息
继续分析:
void __init free_area_init_nodes(unsigned long *max_zone_pfn)
{
unsigned long start_pfn, end_pfn;
int i, nid;
/* Record where the zone boundaries are */
memset(arch_zone_lowest_possible_pfn, 0,
sizeof(arch_zone_lowest_possible_pfn));
memset(arch_zone_highest_possible_pfn, 0,
sizeof(arch_zone_highest_possible_pfn));
start_pfn = find_min_pfn_with_active_regions();
for (i = 0; i < MAX_NR_ZONES; i++) {
if (i == ZONE_MOVABLE)
continue;
end_pfn = max(max_zone_pfn[i], start_pfn);
arch_zone_lowest_possible_pfn[i] = start_pfn;
arch_zone_highest_possible_pfn[i] = end_pfn;
start_pfn = end_pfn;
}
/* Find the PFNs that ZONE_MOVABLE begins at in each node */
memset(zone_movable_pfn, 0, sizeof(zone_movable_pfn));
find_zone_movable_pfns_for_nodes();
/* Print out the zone ranges */
pr_info("Zone ranges:\n");
for (i = 0; i < MAX_NR_ZONES; i++) {
if (i == ZONE_MOVABLE)
continue;
pr_info(" %-8s ", zone_names[i]);
if (arch_zone_lowest_possible_pfn[i] ==
arch_zone_highest_possible_pfn[i])
pr_cont("empty\n");
else
pr_cont("[mem %#018Lx-%#018Lx]\n",
(u64)arch_zone_lowest_possible_pfn[i]
<< PAGE_SHIFT,
((u64)arch_zone_highest_possible_pfn[i]
<< PAGE_SHIFT) - 1);
}
for_each_online_node(nid) {
pg_data_t *pgdat = NODE_DATA(nid);
free_area_init_node(nid, NULL,
find_min_pfn_for_node(nid), NULL);
/* Any memory on that node */
if (pgdat->node_present_pages)
node_set_state(nid, N_MEMORY);
check_for_memory(pgdat, nid);
}
}
这里关键的是free_area_init_node函数,以上就是打印各zone区域的范围。
find_min_pfn_for_node,这个函数不用细看,就是获得该node的最小帧号,从e820探测的数据来看,最开始的内存就是usable,那么最小的帧号是1,地址就是0x1000;继续看free_area_init_node函数。
void __init free_area_init_node(int nid, unsigned long *zones_size,
unsigned long node_start_pfn,
unsigned long *zholes_size)
{
pg_data_t *pgdat = NODE_DATA(nid);
unsigned long start_pfn = 0;
unsigned long end_pfn = 0;
pgdat->node_id = nid;
pgdat->node_start_pfn = node_start_pfn;//该node的最小帧号是1
pgdat->per_cpu_nodestats = NULL;
get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);//遍历该node结点的帧号范围1-0x880000
calculate_node_totalpages(pgdat, start_pfn, end_pfn,zones_size, zholes_size);//实现看下面
alloc_node_mem_map(pgdat);
pgdat_set_deferred_range(pgdat);
free_area_init_core(pgdat);
}
static void __meminit calculate_node_totalpages(struct pglist_data *pgdat,
unsigned long node_start_pfn,
unsigned long node_end_pfn,
unsigned long *zones_size,
unsigned long *zholes_size)
{
unsigned long realtotalpages = 0, totalpages = 0;
enum zone_type i;
for (i = 0; i < MAX_NR_ZONES; i++) {
struct zone *zone = pgdat->node_zones + i;
unsigned long zone_start_pfn, zone_end_pfn;
unsigned long size, real_size;
size = zone_spanned_pages_in_node(pgdat->node_id, i,
node_start_pfn,
node_end_pfn,
&zone_start_pfn,
&zone_end_pfn,
zones_size);//整个node的内存管理范围,包括空洞和所有物理内存大小
real_size = size - zone_absent_pages_in_node(pgdat->node_id, i,
node_start_pfn, node_end_pfn,
zholes_size);//去除空洞的所能管理的物理内存大小,具体没细看
if (size)
zone->zone_start_pfn = zone_start_pfn;
else
zone->zone_start_pfn = 0;
zone->spanned_pages = size;
zone->present_pages = real_size;
totalpages += size;
realtotalpages += real_size;
}
pgdat->node_spanned_pages = totalpages;
pgdat->node_present_pages = realtotalpages;
printk(KERN_DEBUG "On node %d totalpages: %lu\n", pgdat->node_id,
realtotalpages);
}
接下来就是free_area_init_core函数
这个函数一直也是在做初始化操作,初始化一些锁,伙伴系统的链表初始化,managed_pages也被初始化,是从present的内存去除了已经使用的,也就是managed是我们目前实际能使用的内存。
以上zone初始化完成。