【linux 内存管理】zone初始化

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初始化完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值