内存管理(11)物理地址空间的有效性检测主要讲述了Linux内核是如何从memblock.memory
->regions->page->section->mem_section建立物理地址之间关系的。但是我们并没有对mem_section所管理的有效页框创建相应的映射关系以及初始化每个section下的页框属性。所以linux 内核是如何完成这件事的呢?
1.遍历系统中所有可能存在的section,过滤无效的,计算每一个node中有多少个有效的section
2.为每个node申请一个元素数量为有效section个数数量相等的unsigned long*指针数组
3.把每个有效section的unsigned long*指针数组中的指针指向一个数组,这个数组存放了当前section里面所有页的pageblock_flags
4.mem_section.section_mem_map使用了同样的套路
系统中稀疏内存模型分布图
sparse_init函数
sparse_init实现(1)
第17行:设置pageblock_order大小,相关函数实现细节如下↓
sparse_init实现(2)
第30~31行:Linux内核向memblock申请了NR_MEM_SECTIONS * sizeof(unsigned long *)字节的空间,用usemap_map指向其首地址。其中NR_MEM_SECTIONS的含义请参阅内存管理(11)物理地址空间的有效性检测,那这个空间用来做什么,继续往下看。
第34行:alloc_usemap_and_memmap中遍历系统中所有的section,计算出系统中每个node下的有效section数,函数实现如下↓。每遍历完一个node后,就在sparse_early_usemaps_alloc_node中为该node下所有有效的section里面所有的pageblock_flags申请空间,然后遍历每一个section将其对应usemap_map指向对应的pageblock_flags数组首地址,因此上面申请的usemap_map集合中的每一个unsigned long *都会指向对应section下的pageflags数组,相关函数实现如下↓
第38~40行:如果定义了CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOGETHER,申请NR_MEM_SECTIONS * sizeof(struct page *)大小的空间,用map_map指向它。那这个map_map又是用来做什么的,继续往下看
第42~43行:在上文已解释alloc_usemap_and_memma函数。在sparse_early_mem_maps_alloc_node ->sparse_mem_maps_populate_node中如果定义了CONFIG_SPARSEMEM_VMEMMAP那么sparse_mem_maps_populate_node中遍历了每一个有效的section,用前面申请的map_map中的每一个struct page *都建立了对应于section的一系列页框数组的虚拟映射表,比如section[pnum]其对应的map_map[pnum]就会被用来建立一个虚拟映射表,它指向对应section下所有页框的虚拟映射表空间,map_map[pnum]最终会被用去初始化mem_section[pnum].section_mem_map.
第46~48行:遍历所有的section,过滤无效的section
sparse_init实现(3)
第54~58行:如果定义了CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOGETHER就取对应section的map_map,否则对对应section下的map在函数sparse_early_mem_map_alloc->sparse_mem_map_populate中真正的建立映射关系,其实现细节如下↓
第62行:将前面得到的pageblock_flags和map填充到对应section的mem_section中
sparse_init实现(4)
set_pageblock_order函数
set_pageblock_order实现
第107行:定义了超巨页的话pageblock_order = HPAGE_SHIFT - PAGE_SHIFT = 21 - 12 = 9
第109行:没有定义超巨页pageblock_order = MAX_ORDER - 1 = 11 - 1 = 10
alloc_usemap_and_memmap函数:
alloc_usemap_and_memmap实现(1)
第128~132行:遍历系统中所有的section,过滤掉无效section
第138行:同一个node,一个有效section记做一次map
alloc_usemap_and_memmap实现(2)
alloc_usemap_and_memmap实现(3)
第139~162行:计算一个node里面有多少个有效的section需要进行多少次map,并在每遍历完一个node后或对其pageblock_flags进行初始化或对其section_mem_map建立虚拟映射表
sparse_early_usemaps_alloc_node函数
sparse_early_usemaps_alloc_node实现
第259行:计算一个node里面每个有效section的pageblock_flags所占用的空间大小,每个pageblock_flags使用4bit进行存放
为当前node下所有有效section的pageblock_flags申请空间并且用usemap指向它
为每一个section index为下标的usemap_map以此初始化use_map,为最终mem_section中的pageblock_flags初始化做准备
sparse_early_mem_map_alloc->sparse_mem_map_populate函数
sparse_mem_map_populate实现
第302行:通过页框号找到对应物理地址
第304行:根据页框数量偏移得到结尾页框地址
第306行:建立映射关系的实际函数,实现↓
vmemmap_populate函数
vmemmap_populate实现(1)
vmemmap_populate实现(2)
本文的重点在于Linux内核是怎么对mem_section中的section_mem_map和pageblock_flags初始化的。所以这个函数暂不分析,因为实在太晚了,该睡觉了~ 其实有一部分在前面分析过。