Linux 内存管理窥探(12):伙伴系统(初始化)

由于伙伴系统需要使用到 zone 和 zonelist 结构,所以伙伴系统的初始化和 zone & zonelist 关系密切。这里我们回顾一下 zone 和 zonelist 的结构。

zone 是用来表示 Linux 对内存的区域划分的结构,一般的,物理内存会被划分成为 ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM 区域。

我们先来回顾一下 zone 的数据结构:

struct zone {
	/* Read-mostly fields */

	/* zone watermarks, access with *_wmark_pages(zone) macros */
	unsigned long watermark[NR_WMARK];

	unsigned long nr_reserved_highatomic;

	long lowmem_reserve[MAX_NR_ZONES];

#ifdef CONFIG_NUMA
	int node;
#endif
	struct pglist_data	*zone_pgdat;
	struct per_cpu_pageset __percpu *pageset;

#ifndef CONFIG_SPARSEMEM
	unsigned long		*pageblock_flags;
#endif /* CONFIG_SPARSEMEM */

	/* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */
	unsigned long		zone_start_pfn;

	unsigned long		managed_pages;
	unsigned long		spanned_pages;
	unsigned long		present_pages;

	const char		*name;

#ifdef CONFIG_MEMORY_ISOLATION
	unsigned long		nr_isolate_pageblock;
#endif

#ifdef CONFIG_MEMORY_HOTPLUG
	/* see spanned/present_pages for more description */
	seqlock_t		span_seqlock;
#endif

	int initialized;

	/* Write-intensive fields used from the page allocator */
	ZONE_PADDING(_pad1_)

	/* free areas of different sizes */
	struct free_area	free_area[MAX_ORDER];

	/* zone flags, see below */
	unsigned long		flags;

	/* Primarily protects free_area */
	spinlock_t		lock;

	/* Write-intensive fields used by compaction and vmstats. */
	ZONE_PADDING(_pad2_)

	unsigned long percpu_drift_mark;

#if defined CONFIG_COMPACTION || defined CONFIG_CMA
	/* pfn where compaction free scanner should start */
	unsigned long		compact_cached_free_pfn;
	/* pfn where async and sync compaction migration scanner should start */
	unsigned long		compact_cached_migrate_pfn[2];
#endif

#ifdef CONFIG_COMPACTION
	unsigned int		compact_considered;
	unsigned int		compact_defer_shift;
	int			compact_order_failed;
#endif

#if defined CONFIG_COMPACTION || defined CONFIG_CMA
	/* Set to true when the PG_migrate_skip bits should be cleared */
	bool			compact_blockskip_flush;
#endif

	bool			contiguous;

	ZONE_PADDING(_pad3_)
	/* Zone statistics */
	atomic_long_t		vm_stat[NR_VM_ZONE_STAT_ITEMS];
	atomic_long_t		vm_numa_stat[NR_VM_NUMA_STAT_ITEMS];
} ____cacheline_internodealigned_in_smp;

其中:

watermark : 是每个 zone 会计算出 3 个水线,分别是 water_min, water_low, water_high 这些将在回收中用到

lowmem_reserve:zone 预留的内存

zone_pgdat:指向内存节点

zone_start_pfn:zone 中,开始页面的帧号

managed_pages:被伙伴系统管理的页面数量

spanned_pages:zone 包含的页面数量

present_pages:zone 实际管理的页面数量。对于一些体系来说,其等于 spanned_pages

free_area:管理空闲区域的数组,包含链表

lock:保护 zone 结构的自旋锁

lru_lock:LRU 访问的自旋锁

lruvec:LRU 链表集合

vm_stat: zone 计数

标记加粗的部分,均和伙伴系统相关。结构体中的 ZONE_PADDING 是为了性能优化考虑,分布到不同的 cache line。

zone 的初始化参考:
Linux 内存管理窥探(9):内存初始化(bootmem_init)

系统中会有 zonelist 的数据结构,伙伴系统会从 zonelist 开始分配内存,zonelist 有一个 zoneref数组,数组中,有一个成员指向 zone 数据结构。

zoneref 的数组的第一个成员是指向的 zone 是页面分配器的第一个候选者,其他的是第一个候选者失败后才考虑,优先级逐级降低。他的初始化在 build_zonelist_node 中完成,完成后大致为:

先从 HIGHMEM 分配,在去找 NORMAL 分配。 

 

首先了解一下 pageblock 的概念:

一个 pageblock 通常是 2^(MAX_ORDER - 1) 个页面,每一个 pageblock 都有一个对应的 MIGRATE_TYPES 类型。在 zone 的数据结构中,有一个成员指针 pageblock_flags,它用于存放每一个 pageblock 的 MIGRATE_TYPES 类型的内存空间。pageblock_flags 指向的内存空间的大小(即,有多少个 pageblock_flags ),是通过 usemap_size() 函数计算,每一个 pageblock_flags 占用了 4 bits 来存放 MIGRATE_TYPES 类型。

所以呢,Linux 下面的内存,按照 pageblock 进行了 MIGRATE_TYPES 的分类,并存储到了 pageblock_flags 中

在初始化阶段:

start_kernel() -> setup_arch() -> paging_init() -> bootmem_init() -> zone_sizes_init() -> free_area_init_node() -> free_area_init_core() -> memmap_init()

在 memmap_init_zone 函数,对所有的的内存页面全部标记为 MIGRATE_MOVEABLE

 

Buddy System 的初始化在哪里呢?

start_kernel -> mm_init -> mem_init -> free_all_bootmem -> free_low_memory_core_early

把内存块传递到 __free_pages_memory 函数

static void __init __free_pages_memory(unsigned long start, unsigned long end)
{
	int order;

	while (start < end) {
		order = min(MAX_ORDER - 1UL, __ffs(start));

		while (start + (1UL << order) > end)
			order--;

		memblock_free_pages(pfn_to_page(start), start, order);

		start += (1UL << order);
	}
}

可以看到,按照 order 来进行添加。

最后通过 __free_pages_boot_core 调用到 __free_pages 函数,将系统的内存,按照 order 的方式,添加到伙伴系统。

系统初始化阶段,没有被使用到的内存,都以最大阶 10 挂到了 10 的那个链表上。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值