前言
不是特别懂,但是先把自己目前的理解写下来(甚至目前的有些理解都是错的),随着时间的积累再丰富;
内存模型
有三种内存模型:
- Flat Memory:所有内存都是连续的,通过数组管理所有的page,数组的下标就是pfn;所有可以很简单的pfn下标找到对应的page,page可以很简单的找到pfn;
- Discontiguous Memory:非连续内存;没有深入研究,通过node管理page,一个node中page都是连续的;
- Sparse Memory:稀疏内存模型;集中关注这里;
sparse memory
管理思想
存在一个struct mem_section **mem_section
的全局变量,该变量是一个二级指针;
sparse_index_init
函数可以看出mem_section
的组织形式;
sparse_index_init
中使用sparse_index_alloc
分配struct mem_section
的内存,然后赋值给mem_section全局变量进行保存;
- 所以首先可以确定
*mem_section
指向了一个实际的地址,那*mem_section
是一个简单的变量地址,还是一个数组的地址呢?(C语言一个指针比如int *p,这个p可能是一个变量的地址,也有可能是一个数组的地址)
见sparse_index_alloc
可以了解
里面这个内存分配的大小是 SECTIONS_PER_ROOT * sizeof(struct mem_section)
,也就是说这个一级指针指向了一个实际的数组,这个数组的长度是 SECTIONS_PER_ROOT;可以同时计算出array_size是一个page的大小;
那么mem_section
的组织形式就出来了:
如何通过mem_section,进行pfn和page的相互转化
mem_section的section_mem_map存放了该section的起始page(__section_mem_map_addr)
所以__section_mem_map_addr + pfn就是对应的page;
page-__section_mem_map_addr = pfn;
源码实现
创建
arm64_memory_present中遍历memblock,进行memory_present,memory_present中完成了内存的创建
初始化
见sparse_init
,->sparse_init_nid
:
struct page *map,估计就是page,spares_init_one_section,ms->section_mem_map进行了初始page的赋值;
no-map 为什么会报错
现象:
- 使用phys_to_page获取page;再使用page_to_phys将page转化为phys,出错;
page_to_phys 核心是 __page_to_pfn->page_to_section,page_to_section获取section依赖于page->flag;访问flga成员时候出先空指针; - 用户空间分配一个较大的全局变量时,没有该问题;
原因分析
- no-map的内存并没有加入伙伴系统,可以理解为no-map自己实现了一套内存管理方法,它的内存管理只是简单的bitmap置位管理,虚拟地址是通过ioremap获取的,与物理地址是一个简单的偏移;
- 在
__fdt_scan_reserved_mem
扫描reserved-memory
->__reserved_mem_reserve_reg
的时候,会把no-map内存属性的memory从memblock移除掉(early_init_dt_reserve_memory_arch
),不是no-map
的进行reserve; arm64_memory_present
只会对memblock管理的地址进行pfn对应section的创建,所以no-map没有进行管理,进而使用page_to_pfn就会出错;
可以从两个方面理解:1是它没有实际的page;2是它并没有被memblock管理然后创建;
针对现象进行分析
了解了原因之后,分析为什么有这么一个现象;
1是没有实际的page
phys_to_page得到的一个page就是一个错误的page,误打误撞到了用户空间的一个地址,所以当用户空间有一个较大的全局变量时,就不会报错;但是结果有错;
2是它并没有被memblock管理然后创建
由于page是一个错误的page,那后面的访问flag操作就自然而然错误了;
解决方法
直接获取物理地址
不使用pfn_to_page,直接特殊判断使用物理地址;