`
xitong
  • 浏览: 6189009 次
文章分类
社区版块
存档分类
最新评论

Linux伙伴系统(二)--伙伴系统的初始化

 
阅读更多

水平有限,描述不当之处还请指出,转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7611585

伙伴系统的初始化主要是初始化之前介绍的伙伴系统涉及到的数据结构,并且把系统初始化时由bootmem allocator管理的低端内存以及系统的高端内存释放到伙伴系统中去。其中有些和zone相关的域在前面<<Linux节点和内存管理区的初始化>>中已经有所介绍。

在start_kernel()-->paging_init()-->zone_sizes_init()-->free_area_init_nodes()-->free_area_init_node()-->free_area_init_core()-->init_currently_empty_zone()-->zone_init_free_lists()中,free_area的相关域都被初始化

static void __meminit zone_init_free_lists(struct zone *zone)
{
	int order, t;
	for_each_migratetype_order(order, t) {
		/*链表初始化为空链表*/
		INIT_LIST_HEAD(&zone->free_area[order].free_list[t]);
		/*内存块数量初始化为0*/
		zone->free_area[order].nr_free = 0;
	}
}


for_each_migratetype_order(order, t)就是两个嵌套的for循环

#define for_each_migratetype_order(order, type) \
	for (order = 0; order < MAX_ORDER; order++) \
		for (type = 0; type < MIGRATE_TYPES; type++)


start_kernel()-->mm_init()-->mem_init(),负责统计所有可用的低端内存和高端内存,并释放到伙伴系统中

<Init_32.c>

void __init mem_init(void)
{
	int codesize, reservedpages, datasize, initsize;
	int tmp;

	pci_iommu_alloc();

#ifdef CONFIG_FLATMEM
	BUG_ON(!mem_map);
#endif
	/* this will put all low memory onto the freelists */
     /*销毁bootmem分配器,释放bootmem分配器管理的空闲页框和bootmem的位图所占用的页框,
         并计入totalram_pages*/
	totalram_pages += free_all_bootmem();

	reservedpages = 0;
	for (tmp = 0; tmp < max_low_pfn; tmp++)
		/*
		 * Only count reserved RAM pages:
		 */
		if (page_is_ram(tmp) && PageReserved(pfn_to_page(tmp)))
			reservedpages++;

	/*处理高端内存页框*/
	set_highmem_pages_init();

	codesize =  (unsigned long) &_etext - (unsigned long) &_text;
	datasize =  (unsigned long) &_edata - (unsigned long) &_etext;
	initsize =  (unsigned long) &__init_end - (unsigned long) &__init_begin;

	printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, "
			"%dk reserved, %dk data, %dk init, %ldk highmem)\n",
		nr_free_pages() << (PAGE_SHIFT-10),
		num_physpages << (PAGE_SHIFT-10),
		codesize >> 10,
		reservedpages << (PAGE_SHIFT-10),
		datasize >> 10,
		initsize >> 10,
		(unsigned long) (totalhigh_pages << (PAGE_SHIFT-10))
	       );

	printk(KERN_INFO "virtual kernel memory layout:\n"
		"    fixmap  : 0x%08lx - 0x%08lx   (%4ld kB)\n"
#ifdef CONFIG_HIGHMEM
		"    pkmap   : 0x%08lx - 0x%08lx   (%4ld kB)\n"
#endif
		"    vmalloc : 0x%08lx - 0x%08lx   (%4ld MB)\n"
		"    lowmem  : 0x%08lx - 0x%08lx   (%4ld MB)\n"
		"      .init : 0x%08lx - 0x%08lx   (%4ld kB)\n"
		"      .data : 0x%08lx - 0x%08lx   (%4ld kB)\n"
		"      .text : 0x%08lx - 0x%08lx   (%4ld kB)\n",
		FIXADDR_START, FIXADDR_TOP,
		(FIXADDR_TOP - FIXADDR_START) >> 10,

#ifdef CONFIG_HIGHMEM
		PKMAP_BASE, PKMAP_BASE+LAST_PKMAP*PAGE_SIZE,
		(LAST_PKMAP*PAGE_SIZE) >> 10,
#endif

		VMALLOC_START, VMALLOC_END,
		(VMALLOC_END - VMALLOC_START) >> 20,

		(unsigned long)__va(0), (unsigned long)high_memory,
		((unsigned long)high_memory - (unsigned long)__va(0)) >> 20,

		(unsigned long)&__init_begin, (unsigned long)&__init_end,
		((unsigned long)&__init_end -
		 (unsigned long)&__init_begin) >> 10,

		(unsigned long)&_etext, (unsigned long)&_edata,
		((unsigned long)&_edata - (unsigned long)&_etext) >> 10,

		(unsigned long)&_text, (unsigned long)&_etext,
		((unsigned long)&_etext - (unsigned long)&_text) >> 10);

	/*
	 * Check boundaries twice: Some fundamental inconsistencies can
	 * be detected at build time already.
	 */
#define __FIXADDR_TOP (-PAGE_SIZE)
#ifdef CONFIG_HIGHMEM
	BUILD_BUG_ON(PKMAP_BASE + LAST_PKMAP*PAGE_SIZE	> FIXADDR_START);
	BUILD_BUG_ON(VMALLOC_END			> PKMAP_BASE);
#endif
#define high_memory (-128UL << 20)
	BUILD_BUG_ON(VMALLOC_START			>= VMALLOC_END);
#undef high_memory
#undef __FIXADDR_TOP

#ifdef CONFIG_HIGHMEM
	BUG_ON(PKMAP_BASE + LAST_PKMAP*PAGE_SIZE	> FIXADDR_START);
	BUG_ON(VMALLOC_END				> PKMAP_BASE);
#endif
	BUG_ON(VMALLOC_START				>= VMALLOC_END);
	BUG_ON((unsigned long)high_memory		> VMALLOC_START);

	if (boot_cpu_data.wp_works_ok < 0)
		test_wp_bit();

	save_pg_dir();
	/*将前3GB线性地址(即用户地址空间)对应的pgd全局目录项清空*/
	zap_low_mappings(true);
}


free_all_bootmem()的核心函数在<<bootmem allocator>>中已有介绍,这里不再列出

<Highmem_32.c>

void __init set_highmem_pages_init(void)
{
	struct zone *zone;
	int nid;

	for_each_zone(zone) {/*遍历每个管理区*/
		unsigned long zone_start_pfn, zone_end_pfn;

		if (!is_highmem(zone))/*判断是否是高端内存管理区*/
			continue;

		/*记录高端内存管理区的起始页框号和结束页框号*/
		zone_start_pfn = zone->zone_start_pfn;
		zone_end_pfn = zone_start_pfn + zone->spanned_pages;

		nid = zone_to_nid(zone);
		printk(KERN_INFO "Initializing %s for node %d (%08lx:%08lx)\n",
				zone->name, nid, zone_start_pfn, zone_end_pfn);

		/*将高端内存的页框添加到伙伴系统*/
		add_highpages_with_active_regions(nid, zone_start_pfn,
				 zone_end_pfn);
	}
	/*将释放到伙伴系统的高端内存页框数计入totalram_pages*/
	totalram_pages += totalhigh_pages;
}


void __init add_highpages_with_active_regions(int nid, unsigned long start_pfn,
					      unsigned long end_pfn)
{
	struct add_highpages_data data;

	data.start_pfn = start_pfn;
	data.end_pfn = end_pfn;

	/*遍历所有的活动区,调用add_highpages_word_fn()来处理每个活动区*/
	work_with_active_regions(nid, add_highpages_work_fn, &data);
}


void __init work_with_active_regions(int nid, work_fn_t work_fn, void *data)
{
	int i;
	int ret;

	for_each_active_range_index_in_nid(i, nid) {
		ret = work_fn(early_node_map[i].start_pfn,
			      early_node_map[i].end_pfn, data);
		if (ret)
			break;
	}
}

static int __init add_highpages_work_fn(unsigned long start_pfn,
					 unsigned long end_pfn, void *datax)
{
	int node_pfn;
	struct page *page;
	unsigned long final_start_pfn, final_end_pfn;
	struct add_highpages_data *data;

	data = (struct add_highpages_data *)datax;

	/*得到合理的起始结束页框号*/
	final_start_pfn = max(start_pfn, data->start_pfn);
	final_end_pfn = min(end_pfn, data->end_pfn);
	if (final_start_pfn >= final_end_pfn)
		return 0;

	/*遍历活动区的页框*/
	for (node_pfn = final_start_pfn; node_pfn < final_end_pfn;
	     node_pfn++) {
		if (!pfn_valid(node_pfn))
			continue;
		page = pfn_to_page(node_pfn);
		add_one_highpage_init(page, node_pfn);/*将该页框添加到伙伴系统*/
	}

	return 0;

}


static void __init add_one_highpage_init(struct page *page, int pfn)
{
	ClearPageReserved(page);
	init_page_count(page);/*count初始化为1*/
	__free_page(page);     /*释放page到伙伴系统*/
	totalhigh_pages++;      /*高端内存的页数加1*/
}


__free_page()中涉及到的具体操作在后面介绍伙伴系统释放页面时再做分析

在free_area_init_core()-->memmap_init()(-->memmap_init_zone() )-->set_pageblock_migratetype(),将每个pageblock的起始页框对应的struct zone当中的pageblock_flags代表的位图相关区域标记为可移动的,表示该pageblock为可移动的,也就是说内核初始化伙伴系统时,所有的页都被标记为可移动的

void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
		unsigned long start_pfn, enum memmap_context context)
{
	struct page *page;
	unsigned long end_pfn = start_pfn + size;
	unsigned long pfn;
	struct zone *z;

	if (highest_memmap_pfn < end_pfn - 1)
		highest_memmap_pfn = end_pfn - 1;

	z = &NODE_DATA(nid)->node_zones[zone];
	for (pfn = start_pfn; pfn < end_pfn; pfn++) {
		/*
		 * There can be holes in boot-time mem_map[]s
		 * handed to this function.  They do not
		 * exist on hotplugged memory.
		 */
		if (context == MEMMAP_EARLY) {
			if (!early_pfn_valid(pfn))
				continue;
			if (!early_pfn_in_nid(pfn, nid))
				continue;
		}
		page = pfn_to_page(pfn);
		set_page_links(page, zone, nid, pfn);
		mminit_verify_page_links(page, zone, nid, pfn);
		init_page_count(page);
		reset_page_mapcount(page);
		SetPageReserved(page);
		/*
		 * Mark the block movable so that blocks are reserved for
		 * movable at startup. This will force kernel allocations
		 * to reserve their blocks rather than leaking throughout
		 * the address space during boot when many long-lived
		 * kernel allocations are made. Later some blocks near
		 * the start are marked MIGRATE_RESERVE by
		 * setup_zone_migrate_reserve()
		 *
		 * bitmap is created for zone's valid pfn range. but memmap
		 * can be created for invalid pages (for alignment)
		 * check here not to call set_pageblock_migratetype() against
		 * pfn out of zone.
		 */
		 /*如果pfn处在合理的范围,并且该pfn是一个pageblock的起始页框号*/
		if ((z->zone_start_pfn <= pfn)
		    && (pfn < z->zone_start_pfn + z->spanned_pages)
		    && !(pfn & (pageblock_nr_pages - 1)))
			set_pageblock_migratetype(page, MIGRATE_MOVABLE);/*将页框对应的位图域标记为可移动的*/

		INIT_LIST_HEAD(&page->lru);
#ifdef WANT_PAGE_VIRTUAL
		/* The shift won't overflow because ZONE_NORMAL is below 4G. */
		if (!is_highmem_idx(zone))
			set_page_address(page, __va(pfn << PAGE_SHIFT));
#endif
	}
}


static void set_pageblock_migratetype(struct page *page, int migratetype)
{

	/*如果没有开启移动性分类,则所有页都要标记为不可移动的*/
	if (unlikely(page_group_by_mobility_disabled))
		migratetype = MIGRATE_UNMOVABLE;
    
	set_pageblock_flags_group(page, (unsigned long)migratetype,
					PB_migrate, PB_migrate_end);
}


void set_pageblock_flags_group(struct page *page, unsigned long flags,
					int start_bitidx, int end_bitidx)
{
	struct zone *zone;
	unsigned long *bitmap;
	unsigned long pfn, bitidx;
	unsigned long value = 1;

	zone = page_zone(page);
	pfn = page_to_pfn(page);
	/*得到位图的起始地址,即zone->pageblock_flags*/
	bitmap = get_pageblock_bitmap(zone, pfn);
	/*得到pfn对应的位图区域的偏移量*/
	bitidx = pfn_to_bitidx(zone, pfn);
	VM_BUG_ON(pfn < zone->zone_start_pfn);
	VM_BUG_ON(pfn >= zone->zone_start_pfn + zone->spanned_pages);

	/*每个页都需要3个bit来表征其移动性,也就是从start_bitidx到end_bitidx共3位*/
	for (; start_bitidx <= end_bitidx; start_bitidx++, value <<= 1)
		if (flags & value)/*通过flags和value相与确定相关的位是否为1*/
			__set_bit(bitidx + start_bitidx, bitmap);
		else
			__clear_bit(bitidx + start_bitidx, bitmap);
}


分享到:
评论

相关推荐

    linux的内存管理-总结文档

    linux的内存管理分为四个大部分: 1、初始化过程中内存的建立及到伙伴系统的转移; 2、伙伴系统、slab分配器、非连续内存的管理; 3、进程地址空间的内存管理; 4、内存回收;

    linux文件系统初始化过程(2)---挂载rootfs文件系统1

    公司简介|招贤纳士|广告服务|银行汇款帐号|联系方式|版权声明|法律顾问|问题报告|合作伙伴|论坛反馈网站客服杂志客服微博客服400-600-2320 | 北京

    清华大学Linux操作系统原理与应用

    5.2.2 对陷阱门和系统门的初始化 115 5.2.3 中断门的设置 116 5.3 中断处理 116 5.3.1 中断和异常的硬件处理 116 5.3.2 中断请求队列的建立 117 5.3.3 中断处理程序的执行 119 5.3.4 从中断返回 121 5.4 中断的下半...

    疯狂内核之——内核初始化

    5.10 初始化中断处理系统 256 5.10.1 设置APIC中断服务 256 5.10.2 初始化本地软时钟 264 5.10.3 软中断初始化 268 5.10.4 初始化定时器中断 271 5.11 走进start_kernel尾声 273 5.11.1 初始化slab的后续工作 273 ...

    Linux2.6内核标准教程(共计8-- 第1个)

    2.2.5 系统最终初始化 47 2.3 系统引导过程总结 47 第3章 内存管理 50 3.1 基础知识 51 3.1.1 存储器地址 51 3.1.2 分段机制 52 3.1.3 分页机制 59 3.2 内核页表的初始化过程 65 3.2.1 启用分页...

    Linux2.6内核标准教程(共计8--第6个)

    2.2.5 系统最终初始化 47 2.3 系统引导过程总结 47 第3章 内存管理 50 3.1 基础知识 51 3.1.1 存储器地址 51 3.1.2 分段机制 52 3.1.3 分页机制 59 3.2 内核页表的初始化过程 65 3.2.1 启用分页...

    Linux2.6内核标准教程(共计8--第8个)

    2.2.5 系统最终初始化 47 2.3 系统引导过程总结 47 第3章 内存管理 50 3.1 基础知识 51 3.1.1 存储器地址 51 3.1.2 分段机制 52 3.1.3 分页机制 59 3.2 内核页表的初始化过程 65 3.2.1 启用分页...

    Linux2.6内核标准教程(共计8--第3个)

    2.2.5 系统最终初始化 47 2.3 系统引导过程总结 47 第3章 内存管理 50 3.1 基础知识 51 3.1.1 存储器地址 51 3.1.2 分段机制 52 3.1.3 分页机制 59 3.2 内核页表的初始化过程 65 3.2.1 启用分页...

    Linux2.6内核标准教程(共计8--第7个)

    2.2.5 系统最终初始化 47 2.3 系统引导过程总结 47 第3章 内存管理 50 3.1 基础知识 51 3.1.1 存储器地址 51 3.1.2 分段机制 52 3.1.3 分页机制 59 3.2 内核页表的初始化过程 65 3.2.1 启用分页...

    Linux2.6内核标准教程(共计8--第4个)

    2.2.5 系统最终初始化 47 2.3 系统引导过程总结 47 第3章 内存管理 50 3.1 基础知识 51 3.1.1 存储器地址 51 3.1.2 分段机制 52 3.1.3 分页机制 59 3.2 内核页表的初始化过程 65 3.2.1 启用分页...

    Linux2.6内核标准教程(共计8--第2个)

    2.2.5 系统最终初始化 47 2.3 系统引导过程总结 47 第3章 内存管理 50 3.1 基础知识 51 3.1.1 存储器地址 51 3.1.2 分段机制 52 3.1.3 分页机制 59 3.2 内核页表的初始化过程 65 3.2.1 启用分页...

    Linux2.6内核标准教程(共计8--第5个)

    2.2.5 系统最终初始化 47 2.3 系统引导过程总结 47 第3章 内存管理 50 3.1 基础知识 51 3.1.1 存储器地址 51 3.1.2 分段机制 52 3.1.3 分页机制 59 3.2 内核页表的初始化过程 65 3.2.1 启用分页...

    深入分析Linux内核源码

    6.2 Linux内存管理的初始化 6.2.1 启用分页机制 6.2.2 物理内存的探测 6.2.3 物理内存的描述 6.2.4 页面管理机制的初步建立 6.2.5页表的建立 6.2.6内存管理区 6.3 内存的分配和回收 6.3.1 伙伴算法 6.3.2 ...

    linux_kernel_netstack_reading:linux kernel-4.17协议栈源码阅读,阅读linux kernel 4.17 netstack,一旦开始体验可以从内核资源中获得的丰富见解,就很难摆脱Linux的迷恋-linux

    微信群想一起阅读的小伙伴可以加我微信sheepbao-520 ,加入阅读群,备注阅读linux kernelgithub地址目前的进度2018-08-09大概阅读了socket层的代码2018-08-17大概阅读了二层收发包的代码2018-08-22阅读网络设备的...

    新版Android开发教程.rar

    � Android 更像一款桌面环境为 Java 的 Linux 操作系统。有助于 Google 实现其 " 随时随地为每个人提供信 息 " 的企业战略。 HTC HTC HTC HTC Dream/G1 Dream/G1 Dream/G1 Dream/G1 具体配置 硬件 3.17 英寸 HVGA ...

    Linux CentOS服务器搭建与初始化配置教程

    主要为大家详细介绍了Linux CentOS服务器搭建与初始化配置教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

    疯狂内核之——Linux虚拟内存

    1.5.1 初始化临时内核页表 24 1.5.2 永久内核页表的初始化 32 1.5.3 第一次进入用户空间 41 1.5.4 内核映射机制实例 44 1.6 固定映射的线性地址 48 1.7 高端内存内核映射 50 1.8.1 永久内存映射 50 1.8.2 临时内核...

    linux内存管理

    原创,共五章,基于工作中使用的3.10版本内核,包括 内存启动初始化过程,linux内核内存管理,进程虚拟地址管理

    qmi方案:合作伙伴的QMI方案

    qmi场景 合作伙伴的QMI方案 要求 Virtualbox 5.2.x以上 ...输入qmi-scenarios文件夹并初始化文件夹“ shared-content” Linux和OSX cd qmi-scenarios ./init-shared-content.sh 视窗 cd qmi-scenarios ./init-sh

    UnaBoot博客系统-其他

    快速初始化:通过安装向导,快速完成站点初始化工作 标签化建站:尤娜内置了内容标签和内容函数,可以快速的完成模板的制作 多主题:支持多个主题自由切换,快速改变站点风格,而不需重新编译后台代码 Markdown支持:...

Global site tag (gtag.js) - Google Analytics