OK6410A 开发板 (八) 35 linux-5.11 OK6410A 内存管理第三阶段

C setup_arch->paging_init->bootmem_init->memblock_allow_resize返回  - mm_init->mem_init返回
----此时memblock消亡,buddy初始化完成,开启了基于虚拟内时代的 buddy内存管理器时代
  • 流程
setup_arch(&command_line);->paging_init
	bootmem_init
		find_limits(&min_low_pfn, &max_low_pfn, &max_pfn);
		sparse_init				
		zone_sizes_init(min_low_pfn, max_low_pfn, max_pfn);
			// 申请 struct page 所在的空间
			free_area_init(max_zone_pfn);
				pg_data_t *pgdat = NODE_DATA(nid); // &contig_page_data
				free_area_init_node(nid);
					alloc_node_mem_map(pgdat); // struct page 相关的配置
						struct page * map = memblock_alloc_node(size, SMP_CACHE_BYTES, pgdat->node_id);
							// 为 struct page 申请空间
							// struct page 的个数 为 (6000 0000 - 5000 0000)/0x1000
							// memblock_reserve: [0x5fdf6000-0x5fff5fff]
							// 20 0000 个字节
							// 一个 struct page 为 0x20 字节
							// (6000 0000 - 5000 0000)/0x1000 * 0x20
							// 申请的起始地址为 cfdf6000 , 大小为 0x20 0000
						pgdat->node_mem_map = map + offset;
							// map : cfdf6000
							// offset : 0
							// pgdat->node_mem_map : cfdf6000
							
						mem_map = NODE_DATA(0)->node_mem_map;
							// mem_map = pgdat->node_mem_map = cfdf6000



					free_area_init_core(pgdat); // zone相关的设置
						struct zone *zone = pgdat->node_zones + j;
						zone_init_internals(zone, j, nid, freesize);
						setup_usemap(pgdat, zone, zone_start_pfn, size);
							// 申请一块地址,大小 0x20
							// 作用是什么 ? 返回的起始地址 是 0x5fffddc0,对应虚拟地址是 0xcfffddc0
							// memblock_reserve: [0x5fffddc0-0x5fffdddf]
							// 返回的虚拟地址 存储到 了 zone->pageblock_flags 中 
						init_currently_empty_zone
							zone_init_free_lists(zone);
								// order 取值范围 为 [0,...,MAX_ORDER(11)]
								// t 取值范围 为 [MIGRATE_UNMOVABLE(0), ... MIGRATE_TYPES(8)]
								INIT_LIST_HEAD(&zone->free_area[order].free_list[t]);
								zone->free_area[order].nr_free = 0;
							zone->initialized = 1;
						memmap_init(size, nid, j, zone_start_pfn); // 将 创建的 (6000 0000 - 5000 0000)/0x1000 * 0x20 个 struct page 一次 遍历,并 reserved
							start_pfn 	= 50000;
							end_pfn		= 60000;
							size = end_pfn - start_pfn;
							memmap_init_zone(size, nid, zone, start_pfn, range_end_pfn, MEMINIT_EARLY, NULL, MIGRATE_MOVABLE);
								page = pfn_to_page(pfn);
								if (IS_ALIGNED(pfn, pageblock_nr_pages)) set_pageblock_migratetype(page, migratetype);
	empty_zero_page = virt_to_page(zero_page); // empty_zero_page 为 cfff5ec0
		// 之前 zero_page = early_alloc(PAGE_SIZE);
		// zero_page 为 cfff6000
		// 申请的空间刚好在 全部的 struct page  上面
		// 申请了 一物理页(4096B) 空间,返回了 虚拟地址 
		// 以 虚拟地址 zero_page  得到 对应这块地址的 struct page 即 empty_zero_page
		// 函数过程
		// 1. 根据 虚拟地址 zero_page 获取 物理地址A
		// 2. 根据 物理地址A 右移 12 , 获取 pfn
		// 3. 根据 pfn 获取 返回 (mem_map + ((pfn) - ARCH_PFN_OFFSET)) // ARCH_PFN_OFFSET = 50000
		// 4. mem_map 是 第一个page 的地址,结构体 类型为 struct page
		// 5. 返回的是 一个 struct page 的结构体变量地址


	
	// TODO
	__flush_dcache_page(NULL, empty_zero_page);
		// 将缓存中保存的数据向内存执行回写
		// 从而将缓存内容反映到内存,以此确保缓存和内存间的一致性
		// 这里是写了什么 ??? TODO
		
		/*
		setup_processor
			struct proc_info_list *list = lookup_processor(midr); // __v6_proc_info
			cpu_cache = *list->cache; // v6_cache_fns
		
		__flush_dcache_page
			if (!PageHighMem(page)) __cpuc_flush_dcache_area(page_address(page), page_size(page)); 
			// 即 v6_flush_kern_dcache_area , 下面有 对 v6_flush_kern_dcache_area 的 重点解析,全局搜索
			// page_address(page) : cfff 6000
			// page_size(page) 	  : 0x1000
		
		*/


setup_arch
	request_standard_resources(mdesc);
		// 按照树状结构 注册 内存 kernel_code kernel_data

// TODO
build_all_zonelists

// TODO
page_alloc_init
	// 订阅 CPUHP_PAGE_ALLOC_DEAD 信息
	// 注册 CPUHP_PAGE_ALLOC_DEAD 发布时的处理函数 page_alloc_cpu_dead
	// linux-2.6.30.4 是 notify chain机制, cpu_chain
mm_init
	page_ext_init_flatmem
	init_mem_debugging_and_hardening
	report_meminit

	// TODO
	mem_init
		set_max_mapnr(pfn_to_page(max_pfn) - mem_map);
			max_mapnr = struct page 的个数(包括空洞的)
		memblock_free_all
			free_unused_memmap
				// 释放 memblock 块 之间的空洞部分
				// 例如 memblock_add 了三块
				// 0 - 0x1000 0000
				// 0x2000 0000 - 0x3000 0000
				// 0x5000 0000 - 0x6000 0000
				// 那么 0x1000 0000 - 0x2000 0000 就是空洞
				// 那么 0x3000 0000 - 0x5000 0000 也是空洞
				// 第一块空洞对应 下面的函数调用
				// prev_end = 0x1000 0
				// start = 0x2000 0
				// free_memmap(prev_end, start); 
				// free_memmap -> memblock_free
				// memblock_free 把一个逻辑块从memblock.reserved 移除
				// free_unused_memmap  移除的 是 struct page 所在的地址
				// 也就是之前 为 0x1000 0000 - 0x2000 0000 ,即 0x2000 0000 - 0x1000 0000 = 0x1000 0000 大小的空间建立了 
				// 0x1000 0000/0x1000 = 0x10000 个 struct page
				// 这 0x10000 * sizeof(struct page) = 0x10000 * 0x20 = 0x200000
				// 空间, 是之前 memblock_alloc 申请的
				// 但是 这些空间的struct page 没有用(因为对应空洞物理页)
				// 所以将他们释放了
			reset_all_zones_managed_pages
				// 初始化 contig_page_data.node_zones[].managed_pages 为 0
			free_low_memory_core_early
				for_each_reserved_mem_range(i, &start, &end) reserve_bootmem_region(start, end);
					for_each_pfn do{
						init_reserved_page(pfn); // null
						INIT_LIST_HEAD(&page->lru);
						__SetPageReserved(page);
					}
					// 针对 memblock.reserved 中的 每一块 reserved 内存
					// 将内存对应的 struct page 的 flags 成员 __SetPageReserved
					// 被 __SetPageReserved 的 物理页 永远不会 被 当做是 可申请的内存 
					// 被 __SetPageReserved 的 物理页 不在 buddy 的内存管理范围内
				for_each_free_mem_range __free_memory_core(start, end);
					__free_pages_memory
						memblock_free_pages
							// 不同于 memblock_free
							// struct page *page,  	: 	参数1	:	 page的地址
							// unsigned long pfn, 	:	参数2	:	 page frame number
							// unsigned int order	:	参数3	: 	 order : 如果为0 表示 2^0 (即1) 个页 
							__free_pages_core
								// struct page *page,  	: 	参数1	:	 page的地址
								// unsigned int order	:	参数2	: 	 order : 如果为0 表示 2^0 (即1) 个页 
								__ClearPageReserved
								__free_pages_ok
									free_one_page
										__free_one_page
											
					// 针对 memblock.memory 中 memblock.reserved 的补集 中的每一块 内存
					// 将内存对应的 struct page 加入到 free_list
				totalram_pages_add
					atomic_long_add(count, &_totalram_pages);
					// 之前 是 0 
					// 现在 是 ee39
					// end 	  start 	num
					// 50004  50000 	4
					// 50100  50008		F8
					// 5fdbd  51081		ED3C
					// 5fdf6  5fdf5		1
					// 5fffd  5fffe		1

		free_highpages
			// null
		mem_init_print_info
			// 打印如下信息
			// ]Memory: 243940K/262144K available (5120K kernel code, 6569K rwdata, 736K rodata, 1024K init, 2134K bss, 18204K reserved, 0K cma-reserved)
		// 这里本来应有 Virtual kernel memory layout 的打印
		// 但是 去掉了,下面为linux中的commit id
		// 1c31d4e96b8c205fe3aa8e73e930a0ccbf4b9a2b
		
  • memblock 向 buddy 过渡的本质
memblock 时代 物理内存和虚拟内存是怎么管控的
	1. 页表(物理地址到虚拟地址的映射)
		// 对应下面的 early map 类 和 map 类
	2. 按区域 	注册物理内存到 memblock 内存管理器中的 	memblock 变量
		// 对应下面的 memblock 类
buddy 时代 物理内存和虚拟内存是怎么管控的
	1. 页表(物理地址到虚拟地址的映射)
	2. 按物理页 	注册物理内存到 buddy 内存管理器中的 		struct page

memblock 切换 到 buddy , 只需要做
	1. 不需要做页表的映射(因为memblock时代,已经做完了,buddy直接用就行了)
	2. 将 注册到memblock内存管理器memblock变量中 的 物理页 注册到 buddy内存管理器中的struct page
另一个角度看 buddy 初始化
zone_sizes_init
	A B C D E F K
build_all_zonelists
	G H I J
mem_init
	E F L
reset_all_zones_managed_pages
	L

这几个函数主要是 初始化 struct pglist_data contig_page_data; 变量 的成员及 成员的成员

struct pglist_data 											// A
	struct zone node_zones[MAX_NR_ZONES];					// B
		atomic_long_t       managed_pages;					// L
		unsigned long       zone_start_pfn; 				// C
		struct free_area    free_area[MAX_ORDER]; 			// D
			struct list_head    free_list[MIGRATE_TYPES]; 	// E
			unsigned long       nr_free;					// F
	struct zonelist node_zonelists[MAX_ZONELISTS];			// G
		struct zoneref _zonerefs[MAX_ZONES_PER_ZONELIST + 1];// H
			struct zone *zone;								// I
			int zone_idx;									// J
	struct page *node_mem_map;								// K
	
重点函数解析
  • v6_flush_kern_dcache_area
/*
 *	v6_flush_kern_dcache_area(void *addr, size_t size)
 *
 *	Ensure that the data held in the page kaddr is written back
 *	to the page in question.
 *
 *	- addr	- kernel address
 *	- size	- region size
 */
ENTRY(v6_flush_kern_dcache_area)
	// r0 = cfff 6000
	// r1 = 0x1000
	add	r1, r0, r1 							
	// r1 = cfff 7000
	bic	r0, r0, #D_CACHE_LINE_SIZE - 1		
	// D_CACHE_LINE_SIZE = 32
	// 32 -1 = 1F
	// r0 = r0 bit_clear 1F = cfff 6000


	// HARVARD_CACHE 已经 define

1:
#ifdef HARVARD_CACHE
	mcr	p15, 0, r0, c7, c14, 1		@ clean & invalidate D line
	// 写cp15 c7
	// 写入的数据 依次是 
	// cfff 6000
	// cfff 6020
	// cfff 6040
	// ...
	// cfff 6fe0

	// 作用是 Clean and invalidate data cache line
	// Clean :
	// 适用于write back 数据缓存
	// 意味着如果缓存线包含尚未写入主内存的存储数据,则它现在将写入主内存
	// 并且该行被标记为干净。
	// invalidate :
	// 表示缓存线(或缓存中的所有行)被标记为无效。
	// 在将该行重新分配到某个地址之前,该行不会发生缓存命中。
	// 对于写回数据缓存,这不包括清除缓存线,除非另有说明。
	// cache line :
	// 为了尽量减少控制信息的存储量,空间局部性属性用于将多个位置分组到同一标签下。
	// 这个内存位置的逻辑块通常称为缓存线,通常为32字节长。

	// Cache写机制分为write through和write back两种
	// write back :
	// Write-back(回写模式)在数据更新时只写入缓存Cache。
	// 只在数据被替换出缓存时,被修改的缓存数据才会被写到后端存储。
	// 此模式的优点是数据写入速度快,因为不需要写存储;
	// 缺点是一旦更新后的数据未被写入存储时出现系统掉电的情况,数据将无法找回。
	// Write-through : 
	// Write-through(直写模式)在数据更新时,同时写入缓存Cache和后端存储。
	// 此模式的优点是操作简单;缺点是因为数据修改需要同时写入存储,数据写入速度较慢

	// cache 写机制 之前被设置成了什么 ?? TODO
	// early_mm_init 中有打印 
	// Memory policy: Data cache writeback
	// early_mm_init 中不是设置,而是显示,设置在哪里,TODO
	// write back 的设置有几个设置点
	// 1. cp15 c1 // 控制开 write buffer/cache
	// 2. pmd 	// 控制 pmd的bit[3:2] 为 11 
	// 3. pte	// 控制 pte的bit[3:2] 为 11


	// 内存 cfff 6000 - cfff 7000 被加载入缓存
	// 如果 缓存中的数据有变化,但是 因为 cache 机制 为 write back, 所以此时还没写入内存
	// mcr	p15, 0, r0, c7, c14, 1 作用是
	// 将 缓存中的数据写到 内存 cfff 6000 - cfff 7000

	// 但是 内存 cfff 6000 - cfff 7000 中的数据 在 该操作前后 全部为 0(前后一致) 
	// 也就是说 没有修改过 内存 cfff 6000 - cfff 7000, 为什么还要 做cache 与内存的同步
#else
	
#endif	
	add	r0, r0, #D_CACHE_LINE_SIZE
	// D_CACHE_LINE_SIZE = 32 = 0x20
	cmp	r0, r1
	blo	1b

	// r0 < r1 则跳转
#ifdef HARVARD_CACHE
	mov	r0, #0
	mcr	p15, 0, r0, c7, c10, 4
	// 写 cp15 c7
	// 写入的数据是 0

	// Data Synchronization Barrier (formerly Drain Write Buffer)
	// Data synchronization barrier :
	// 以前的数据写屏障,数据写载体(DWB),现在叫DSB
	// 数据同步屏障可以在特权和用户操作模式下执行

	// B2.6.2数据同步屏障(DSB)
	// 	特点:
	// 		在DSB完成之前,DSB后面的任何指令都不能执行。

	// 	CP15寄存器7注:该操作历来被称为DrainWriteBuffer或DataWriteBarrier(DWB)。
	// 	在ARMv6中,这些名称(以及DWB的使用)被弃用,取而代之的是新的DataSynchronizationBarrier名称和DSB。DSB更好地反映了ARMv6中提供的功能;

	// 	它在架构上定义为包括所有缓存、TLB和分支预测维护操作以及显式内存操作。
		
	// 	数据同步屏障操作是一种特殊的内存屏障。

	// 	DSB操作在以下情况下完成:
	// 		•在本指令之前,所有显式内存访问完成.
	// 		•完成本指令之前的所有缓存、分支预测和TLB维护操作。

	// 	DataSynchronizationBarrier的编码在B6-19页的Register 7:cache management functions中描述。


#endif
	ret	lr

  • __SetPageReserved 的定义
include/linux/page-flags.h 	L275
275 static __always_inline void __SetPage##uname(struct page *page)     \            
276     { __set_bit(PG_##lname, &policy(page, 1)->flags); } 



 static inline __attribute__((__gnu_inline__)) 
 __attribute__((__unused__)) 
 __attribute__((__no_instrument_function__)) 
 __attribute__((__always_inline__)) 
 void __SetPageReserved(struct page *page) 
 { 
 	__set_bit(PG_reserved, &({ ((void)(sizeof(( long)(1 && PageCompound(page))))); 
 	({ ((void)(sizeof(( long)(PagePoisoned(page))))); page; }); })->flags); 
 }

// 注意 uname 与 lname

include/linux/mm_types.h 	L69

struct page {
	...
	union {
		...
		unsigned int page_type;
		...
	};
	...
};


include/linux/page-flags.h 	L103
enum pageflags {
	...
	PG_reserved,
	...
};

  • __free_one_page
__free_one_page
 996 static inline void __free_one_page(struct page *page,                            
 997         unsigned long pfn,                                                       
 998         struct zone *zone, unsigned int order,                                   
 999         int migratetype, fpi_t fpi_flags)                                        
__free_one_page
	continue_merging: // 像 high level 合并
	while (order < max_order) {
		...
	}
	done_merging:
	to_tail = xxx;
	if (to_tail) add_to_free_list_tail(page, zone, order, migratetype);
	else 		 add_to_free_list(page, zone, order, migratetype);
		struct free_area *area = &zone->free_area[order];
		list_add(&page->lru, &area->free_list[migratetype]);
		area->nr_free++;
	
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值