Linux内存管理之mem_map对象.md

2023-04-21 20:17:19 浏览数 (1)

在linux内核中,所有的物理内存都用struct page结构来描述,这些对象以数组形式存放,而这个数组的地址就是mem_map。内核以节点node为单位,每个node下的物理内存统一管理,也就是说在表示内存node的描述类型struct pglist_data中,有node_mem_map这个成员,其针对平坦型内存进行描述(CONFIG_FLAT_NODE_MEM_MAP),与此相反的是SPARSEMEM,其稀疏性内存描述。

mem_map的作用

mem_map是一个数组,存放了所有的页描述符。一个页对应一个页描述符。

mem_map的定义

代码语言:javascript复制
/* linuxmmmemory.c */

#ifndef CONFIG_NEED_MULTIPLE_NODES
/* use the per-pgdat data instead for discontigmem - mbligh */
unsigned long max_mapnr;
struct page *mem_map;

EXPORT_SYMBOL(max_mapnr);
EXPORT_SYMBOL(mem_map);
#endif

dump_stack的输出

代码语言:javascript复制
[    0.000000] [alloc_node_mem_map 5920] .
[    0.000000] CPU: 0 PID: 0 Comm: swapper Not tainted 4.9.51-1.2 #136
[    0.000000] Hardware name: Broadcom STB (Flattened Device Tree)
[    0.000000] [<c0210710>] (unwind_backtrace) from [<c020bb54>] (show_stack 0x10/0x14)
[    0.000000] [<c020bb54>] (show_stack) from [<c04fc93c>] (dump_stack 0x84/0x98)
[    0.000000] [<c04fc93c>] (dump_stack) from [<c09b6e4c>] (alloc_node_mem_map.constprop.8 0x28/0xd0)
[    0.000000] [<c09b6e4c>] (alloc_node_mem_map.constprop.8) from [<c0e0f10c>] (free_area_init_node 0xf0/0x38c)
[    0.000000] [<c0e0f10c>] (free_area_init_node) from [<c0e065f8>] (bootmem_init 0x16c/0x1b0)
[    0.000000] [<c0e065f8>] (bootmem_init) from [<c0e08ac0>] (paging_init 0xd28/0xd90)
[    0.000000] [<c0e08ac0>] (paging_init) from [<c0e0429c>] (setup_arch 0x61c/0xc24)
[    0.000000] [<c0e0429c>] (setup_arch) from [<c0e009d0>] (start_kernel 0xb8/0x404)
[    0.000000] [<c0e009d0>] (start_kernel) from [<00008090>] (0x8090)

/*
代码调用流程:
start_kernel --> setup_arch --> paging_init --> bootmem_init --> free_area_init_node --> alloc_node_mem_map

由代码调用流程可以看出,mem_map的初始化,是在初始化node时做的。也就是说,mem_map是node下一级的一个概念。
*/

alloc_node_mem_map源码分析

代码语言:javascript复制
/* linuxmmpage_alloc.c */
static void __ref alloc_node_mem_map(struct pglist_data *pgdat)
{
	unsigned long __maybe_unused start = 0;
	unsigned long __maybe_unused offset = 0;

    /* 空节点判断 */
	/* Skip empty nodes */
	if (!pgdat->node_spanned_pages)
		return;

#ifdef CONFIG_FLAT_NODE_MEM_MAP

    /* start 是起始页帧号 */
	start = pgdat->node_start_pfn & ~(MAX_ORDER_NR_PAGES - 1);
	offset = pgdat->node_start_pfn - start;

	/* ia64 gets its own node_mem_map, before this, without bootmem */
    /* 如果当前node没有分配node_mem_map, 则分配 */
	if (!pgdat->node_mem_map) {
		unsigned long size, end;
		struct page *map;

		/*
		 * The zone's endpoints aren't required to be MAX_ORDER
		 * aligned but the node_mem_map endpoints must be in order
		 * for the buddy allocator to function correctly.
		 */
        /* end 是终点页帧号 */
		end = pgdat_end_pfn(pgdat);
		end = ALIGN(end, MAX_ORDER_NR_PAGES);

		// (end - start)计算当前节点有多少个page
		size =  (end - start) * sizeof(struct page);
		map = alloc_remap(pgdat->node_id, size);
		if (!map)
			map = memblock_virt_alloc_node_nopanic(size,
							       pgdat->node_id);
		pgdat->node_mem_map = map   offset;
	}
#ifndef CONFIG_NEED_MULTIPLE_NODES
	/*
	 * With no DISCONTIG, the global mem_map is just set as node 0's
	 */
	if (pgdat == NODE_DATA(0)) {
        /* 当前节点是0号节点, 则初始化mem_map*/
		mem_map = NODE_DATA(0)->node_mem_map;
#if defined(CONFIG_HAVE_MEMBLOCK_NODE_MAP) || defined(CONFIG_FLATMEM)
		if (page_to_pfn(mem_map) != pgdat->node_start_pfn){
			mem_map -= offset;
			}
#endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */
	}
#endif
#endif /* CONFIG_FLAT_NODE_MEM_MAP */
}

/*
这个函数只是给mem_map分配了内存空间,并没有初始化里面的数据
*/

0 人点赞