博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
OK6410A 开发板 (八) 47 linux-5.11 OK6410A 物理虚拟内存的管理
阅读量:4285 次
发布时间:2019-05-27

本文共 9704 字,大约阅读时间需要 32 分钟。

物理虚拟内存管理理论

在 mmu 开启之后, 存在了两种内存	物理内存	虚拟内存就需要对 这两种内存 进行管理我们用内存的时候,需要	1. 物理内存	2. 虚拟内存 // 与 物理内存对应的虚拟内存对物理内存的管理方法有4种(根据内存模型不同而不同)	CONFIG_FLATMEM	CONFIG_DISCONTIGMEM	CONFIG_SPARSEMEM_VMEMMAP	CONFIG_SPARSEMEM对虚拟内存的管理方法有4种,我们关注以下过程	直接映射区	vmalloc 动态映射区 	持久映射区 kmap 	临时映射区 fixmap (kmap_atomic 临时内核映射)注意 : 在运行时,	物理内存的管理方法只有一种,配置哪种就是哪种	虚拟内存的管理方法有4种对于内存管理,我们关注	// 1. 存放	// 2. 申请	// 3. 释放

level 1 物理内存的管理

  • 第一种物理内存管理 CONFIG_FLATMEM
1. 初始化	配置了 CONFIG_FLAT_NODE_MEM_MAP 之后 pglist_data 结构体 中 才会有 node_mem_map 成员		// 为所有的物理内存创建 struct page (每4KB创建一个)	pg_data_t *pgdat = NODE_DATA(nid); // &contig_page_data	struct page * map = memblock_alloc_node(size, SMP_CACHE_BYTES, pgdat->node_id);	pgdat->node_mem_map = map + offset;			reserve_bootmem_region		// 设置 struct page 的 flags 成员	__free_memory_core		// 将 struct page 挂到 free_list 成员中		// 此时 struct page 也在 node_mem_map  中2. 申请	get_page_from_freelist3. 释放	add_to_free_list

level 1 虚拟内存的管理

  • 第一种 线性映射/直接映射区
1.初始化	map_lowmem2.申请/获取	page_to_virt(page)	page_address(page);3.释放	不需要释放
特点	1.线性映射关系,管理简单	2.唯一的lowmemory 映射的区域	3.映射一旦完成,系统运行期间不会改变.其他的4种都会改变
应用场景	TODO
  • 第二种 vmalloc 动态映射区
1. 初始化	vmalloc_init2. 申请	__alloc_vmap_area(size, align, vstart, vend);3. 释放	va = __find_vmap_area((unsigned long)addr);	free_unmap_vmap_area(va);
特点	1.最灵活,其他区不能满足的需求,都由该区满足	2.返回的虚拟地址空间是连续的(4K 4K的连续)	3.地址范围固定,VMALLOC_START - VMALLOC_END
应用场景	TODO
  • 第三种 持久映射区 kmap
1. 初始化	pkmap_page_table = early_pte_alloc(pmd_off_k(PKMAP_BASE), PKMAP_BASE, _PAGE_KERNEL_TABLE);2. 申请	vaddr = map_new_virtual(page); 		vaddr = PKMAP_ADDR(last_pkmap_nr);		set_pte_at(&init_mm, vaddr, &(pkmap_page_table[last_pkmap_nr]), mk_pte(page, kmap_prot));3. 释放	vaddr = (unsigned long)page_address(page);	nr = PKMAP_NR(vaddr);	// 此时有一个等待队列,不知道干啥的
特点	1.一次只能映射一页	2.可能会引起睡眠
应用场景	TODO
  • 第四种 临时映射区 fixmap (kmap_atomic)
1. 初始化	early_pte_alloc(pmd_off_k(FIXADDR_START), FIXADDR_START, _PAGE_KERNEL_TABLE);2. 申请	idx = arch_kmap_local_map_idx(kmap_local_idx_push(), pfn);	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);	pteval = pfn_pte(pfn, prot);	arch_kmap_local_set_pte(&init_mm, vaddr, kmap_pte - idx, pteval);3. 释放	idx = arch_kmap_local_unmap_idx(kmap_local_idx(), addr);	pte_clear(&init_mm, addr, kmap_pte - idx);
特点	1. 返回的虚拟地址是固定的,FIXADDR_TOP_START - FIXADDR_TOP 中 偏移量 为 FIX_KMAP_BEGIN - FIX_KMAP_END 的 区间	2. 每个cpu都有属于自己的临时映射区,大小为 KM_TYPE_NR * 4KB	3. 临时映射区的区间很小,管理简单	4. 寻找到空闲的虚拟空间的速度快	5. 不会睡眠	6. 临时映射区的使用是有限制的,映射使用完成后必须马上去除映射.// 因为映射中调用 preempt_disable 关闭了内核抢占
应用场景	TODO
  • 第五种 VMA
1. 初始化	2. 申请	addr = get_unmapped_area(file, addr, len, pgoff, flags);3. 释放	vma = find_vma(mm, start); // 类似于 vmalloc 的 va = __find_vmap_area((unsigned long)addr);	// unmap_region(mm, vma, prev, start, end);	remove_vma_list(mm, vma); // 类似于 vmalloc 的 free_unmap_vmap_area(va);
特点	1.只有这一种能用于 进程用户空间虚拟地址的管理	2.地址范围在 0x0000 0000 - 0xC000 0000
应用场景	TODO

level2 内存管理的实现

在处理 用户需求(申请内存)时,要处理	1. 物理内存的申请	2. 虚拟内存的申请实现有 buddy vmalloc , 这些实现 是 对 level1虚拟内存的管理 level1物理内存的管理 的封装在内核配置一定的基础上, buddy管理了 运行时的物理内存管理	配置 管理了 物理内存 // 设置了管理内存的方式	buddy 针对这些配置 实现了 不同的 物理内存管理,但在运行时,只有一种 物理内存管理,即 配置确定后的buddy
buddy很奇怪,封装了如下,但并不是简单的封装 level1	第一种 物理内存的管理		// 管理的物理内存来自于 buddy 管理的 所有的ZONE的 struct page	没有封装 虚拟内存的管理buddy 提供的api中	alloc_pages/alloc_page 				封装了物理内存的管理,没有封装虚拟内存的管理	__get_free_pages/__get_free_page   	封装了物理内存的管理,并封装了虚拟内存的管理 		// 通过 page_address(page);封装了 虚拟内存的管理		// __get_free_page() 分配的是物理地址,而返回的则是虚拟地址(虽然这听上去有些别扭)可以这么说:	buddy 实现了对物理内存的管理,并没有实现对虚拟内存的管理	但在buddy输出的api里面,有些api封装了对虚拟内存的管理	但是我们一般说 buddy 管理的仅仅是物理内存,而不包括虚拟内存
vmalloc 封装了	第一种 物理内存的管理		// 管理的物理内存来自于 buddy 管理的 ZONE_HIGHMEM 中的 struct page		// 每次只申请一个 page ,所以 申请出来的多块内存不连续	第二种 虚拟内存的管理
slab 是 很特殊的,不是 这几种合成的产物	物理内存的管理		// 在 buddy(不带封装虚拟内存的buddy)的基础上,将struct page 分成几块 来管理		// kmalloc会根据申请的大小来选择基于slub分配器或者基于Buddy System来申请连续的物理内存		// 基于 slub 分配的话, 一个 对象 小于 4KB , 所以在 4KB 内 物理内存是连续的		// 基于 buddy 分配的化, 由于一次申请多个 4KB块, 所以如果申请成功,则 多个4KB块内 是连续的	第一种虚拟内存的管理		// 通过 page_address(page);封装了 虚拟内存的管理 ??? 没有找到代码kmalloc 基于 slab

其他

  • 物理内存管理还是虚拟内存管理
buddy 属于 物理内存管理还是虚拟内存管理	物理内存管理物理内存是什么 : 	内存中所有的页框	物理内存怎么管理	1个页框 对应 一个 struct page  结构体 , 存储 在 mem_map 数组中.			物理地址 在 struct page 中的 体现		体现在 struct page 的 地址上 : page_to_phys(page)		// 虽然这里说的是物理内存的管理,但是alloc返回的却是虚拟地址.	// 查找 struct page 对应的 虚拟地址 由 page_address 提供实现	虚拟地址 在 struct page 中的 体现		如果是低端内存,体现在 struct page 的 地址上 : page_to_virt(page)				// 高端内存 建立映射的方法有三种,为什么只在 一种(kmap) 中 找 虚拟地址		如果是高端内存,体现在 在 持久内核映射kmap机制 建立的 数据结构 中查找			如果之前做了映射(pam->page = page),返回  pam->virtual			如果之前没做映射,返回 NULL.虚拟内存是什么	0G-3G 用户空间		每个用户进程 有一个		映射是怎么做的?		映射关系是怎么保存的?	3G-4G 内核空间		内核/用户进程内核态/内核线程 共用一个				分类:			低端内存				虚拟地址 C000 0000 - EF80 0000 线性映射 物理地址 (0-760MB) 0000 0000 - 2F80 0000				映射在页表中,映射关系保存在哪里			高端内存				虚拟地址 EF80 0000 - FFFF FFFF (264MB)动态映射 物理地址 (760M-4G)2F80 0000 - FFFF FFFF				映射在表中(三种机制建立的),映射关系保存在哪里?					需要在 建立页表的过程中,需要追代码来看映射关系.										1. 持久内核映射 : page_address_htable					2. 临时内核映射 : 每个cpu有几个?					3. 非连续内存分配 : 虚拟内存怎么管理
  • 物理内存和虚拟内存的管理
内存的管理	物理内存管理原理	// 物理内存 的关系(phy_addr与page)由 固定 一次函数关系 来描述	// 不同(可选的有四种)的 模型 , 有不同的 函数关系	// 在 CONFIG_FLATMEM 模型下,函数关系如下	// phy_addr = (addr_of(page) - addr_of(mem_map) + ARCH_PFN_OFFSET) << PAGE_SHIFT	// mem_map 为 一个  固定值	// ARCH_PFN_OFFSET =  __pv_phys_pfn_offset		物理内存管理实现		1. buddy			物理地址 在 struct page 中的 体现				体现在 struct page 的 地址上 : page_to_phys(page)		2. slab			slab 没有 对物理内存的管理,完全是 基于 buddy // TODO,基于buddy的物理内存管理,将物理内存管理粒度变的更小		3. vmalloc			vmalloc 没有 对物理内存的管理,完全是 基于 buddy		虚拟内存管理原理	// 虚拟内存 由 以下四种管理	// 直接映射区								低端内存	// vmalloc 动态映射区 					内存连续	// 持久映射区 kmap 						进程空间	// 临时映射区 基于fixmap (kmap_atomic) 	不会睡眠/快速`		虚拟内存:低端内存实现		1. buddy			虚拟地址 在 struct page 中的 体现				如果是低端内存,体现在 struct page 的 地址上 : page_to_virt(page)	//直接映射区		2. slab			slab 没有对虚拟内存的管理,完全是基于 buddy		3. vmalloc			在这个维度不存在	虚拟内存:高端内存实现		1. buddy			如果是高端内存,在 持久内核映射kmap机制 建立的 数据结构 page_address_htable 中查找 到的 pam->virtual 变量 体现 // 持久映射区				如果之前做了映射(pam->page = page),返回  pam->virtual				如果之前没做映射,返回 NULL.		2. slab			slab 没有对虚拟内存的管理,完全是基于 buddy		3. vmalloc // vmalloc 动态映射区			虚拟地址在 struct vmap_area 结构体 中的 va_start 成员 体现			struct vmap_area 结构体被挂载到 vmap_area_root和vmap_area_list
  • 内存模型 对 物理内存管理 的 影响
内存模型	https://zhuanlan.zhihu.com/p/220068494#define page_to_pfn __page_to_pfn#define page_to_phys(page)  (__pfn_to_phys(page_to_pfn(page)))不同的 memory_model 下 函数关系 不同 , 存在有4个模型,选一个即可CONFIG_FLATMEM	#define __page_to_pfn(page)	((unsigned long)((page) - mem_map) + \					 ARCH_PFN_OFFSET)CONFIG_DISCONTIGMEM	#define __page_to_pfn(pg)						\	({	const struct page *__pg = (pg);					\		struct pglist_data *__pgdat = NODE_DATA(page_to_nid(__pg));	\		(unsigned long)(__pg - __pgdat->node_mem_map) +			\		 __pgdat->node_start_pfn;					\	})CONFIG_SPARSEMEM_VMEMMAP	#define __page_to_pfn(page)	(unsigned long)((page) - vmemmap)CONFIG_SPARSEMEM	#define __page_to_pfn(pg)					\	({	const struct page *__pg = (pg);				\		int __sec = page_to_section(__pg);			\		(unsigned long)(__pg - __section_mem_map_addr(__nr_to_section(__sec)));	\	})

物理虚拟内存管理实现1:buddy的实现

能直接用 用 alloc_pages 从 ZONE_DMA/ZONE_NORMAL 	返回 struct page	并能通过 page_to_phys(page) 得到 虚拟地址能直接  用 alloc_pages 从 ZONE_HIGHMEM 			返回 struct page	不能通过 page_to_phys(page) 得到 虚拟地址	因为 page_to_phys(page) 只支持 低端内存(虚拟内存概念) 对应的 物理页		通过 查找 几种方法(A) 建立的映射关系		如果 查找了映射关系,则返回虚拟地址		如果 没查到映射关系,则返回NULL	此时可以有 几种方法(A) 建立映射	1. 持久内核映射	: kmap/kunmap				: 利用了与 fixmap 同时建立的 pkmap_page_table // page_address_htable // https://blog.csdn.net/u011011827/article/details/116916526		// 一级页表 在 0x50007xxx 			// 二级页表 在 &(pkmap_page_table[last_pkmap_nr]) // // pkmap_page_table = early_pte_alloc(pmd_off_k(PKMAP_BASE)		// 虚拟地址 在 memory layout 的 PKMAP 区域 , PKMAP 区域 在 配置了 CONFIG_HIGHMEM 之后 才会存在 ,低于0xc000 0000		// 虚拟地址范围 PKMAP_BASE - PKMAP_BASE+ LAST_PKMAP*4K	2. 临时内核映射	: kmap_atomic/kunmap_atomic	: 利用了 fixmap		// 一级页表 在 0x50007xxx		// 二级页表 在 kmap_pte - idx  // pte_t *kmap_pte = kmap_get_pte(); // virt_to_kpte(__fix_to_virt(FIX_KMAP_BEGIN));		// 虚拟地址 在 memory layout 的 fixmap 区域		// 虚拟地址范围 FIXADDR_TOP_START - FIXADDR_TOP	3. 非连续内存分配	: vmap/vunmap				: 是 vmalloc 相关的函数		// 一级页表 在 0x50007xxx		// 二级页表 在 pte = pte_alloc_kernel_track(pmd, addr, mask);		// 虚拟地址 在 memory layout 的 vmalloc 区域		// 虚拟地址范围 VMALLOC_START - VMALLOC_END		// map_kernel_range -> map_kernel_range_noflush -> vmap_p4d_range -> vmap_pud_range -> vmap_pmd_range		//	vmap_pmd_range		//		pmd_alloc_track		//			// 申请写入的 一级页表的地址 (位于 0x50007xxx)		//		vmap_pte_range		//			// 处理二级页表		//	vmap_pte_range		//		pte_alloc_kernel_track		//			// 申请写入的 二级页表的地址		//			// 写入一级页表		//		set_pte_at		//			// 写入二级页表		// 二级页表的申请		// pte_alloc_kernel_track			__pte_alloc_kernel				pte_alloc_one_kernel					__pte_alloc_one_kernel						__get_free_page(GFP_PGTABLE_KERNEL);				pmd_populate_kernel					__pmd_populate						pmdp[0] = __pmd(pmdval);						pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t));

其他

  • 内核高端内存映射机制 与 fixmap(固定映射) 的关系与区别
fixmap(固定映射)		临时的 : 在系统启动过程early_fixmap_shutdown中,创建的映射关系(pte)被会清0		temporary-fix-map		temporary fixed address		在 fixed_addresses 枚举体 中  0 - __end_of_permanent_fixed_addresses 		例如 FIX_EARLYCON_MEM_BASE 对应的 earlycon / early_ioremap 			// 有人说 FIX_EARLYCON_MEM_BASE 属于 permanent fixed address			// 但是 实验结果是 earlycon 做的映射 在 early_fixmap_shutdown 中被 清0 了	永久的 : 创建好的不会被清0		permanent-fix-map		permanent fixed address		在 fixed_addresses 枚举体 中 __end_of_permanent_fixed_addresses - __end_of_fixed_addresses(__end_of_fixmap_region/或__end_of_early_ioremap_region 哪个大是哪个)		例如 FIX_KMAP_BEGIN/FIX_KMAP_END内核高端内存映射机制	临时的 : 		kmap_atomic 不会阻塞		临时映射,一个cpu core只能建立16个映射,一般建立后用完会马上清除,因为空间不够		基于 fixmap 的 FIX_KMAP_BEGIN/FIX_KMAP_END		返回地址 : FIXADDR_START - FIXADDR_TOP		有人把这种方式 成为 固定映射(fixmap) 		固定映射和临时映射不能等同,严格来说临时映射只是固定映射的一段FIX_KMAP_BEGIN到FIX_KMAP_END区间	永久/持久的 : 		persisent-kernel-mapping		kmap 会阻塞,不能用在中断处理函数和可延迟函数		长期映射,不会因为空间不够而清0 原来建立的,可以建立很多映射		基于 与fixmap 几乎同时创建的 pkmap_page_table		返回地址: PKMAP_BASE - FIXADDR_START
你可能感兴趣的文章
烧写uboot
查看>>
QT安装
查看>>
QtCreator介绍
查看>>
QT工程实例
查看>>
pkg-config
查看>>
Linux内核分析-1/反汇编(堆栈)
查看>>
Linux内核分析-2/时间片轮转多道程序
查看>>
Linux内核分析-4/5/系统调用
查看>>
c/c++常见关键字
查看>>
C++内存地址分配和内存区划分简介
查看>>
C++数值交换
查看>>
指针数组、数组指针、函数指针、指针函数
查看>>
float,double在内存中的存储方式
查看>>
int main(int argc,char* argv[])详解
查看>>
C++打印地址
查看>>
ARM处理器比较:A8/A9
查看>>
ARM处理器工作模式
查看>>
ARM处理器寄存器
查看>>
汇编语言学习
查看>>
ARM寻址方式
查看>>