内存管理(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使用了同样的套路

内存基址和内存地址(内存管理12物理地址空间的有效性检测)(1)

系统中稀疏内存模型分布图

sparse_init函数

内存基址和内存地址(内存管理12物理地址空间的有效性检测)(2)

  • 第17行:设置pageblock_order大小,相关函数实现细节如下↓

  • 内存基址和内存地址(内存管理12物理地址空间的有效性检测)(3)

    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

    内存基址和内存地址(内存管理12物理地址空间的有效性检测)(4)

    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中

    内存基址和内存地址(内存管理12物理地址空间的有效性检测)(5)

    sparse_init实现(4)

    set_pageblock_order函数

    内存基址和内存地址(内存管理12物理地址空间的有效性检测)(6)

    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函数:

    内存基址和内存地址(内存管理12物理地址空间的有效性检测)(7)

    alloc_usemap_and_memmap实现(1)

    • 第128~132行:遍历系统中所有的section,过滤掉无效section

    • 第138行:同一个node,一个有效section记做一次map

    内存基址和内存地址(内存管理12物理地址空间的有效性检测)(8)

    alloc_usemap_and_memmap实现(2)

    内存基址和内存地址(内存管理12物理地址空间的有效性检测)(9)

    alloc_usemap_and_memmap实现(3)

    • 第139~162行:计算一个node里面有多少个有效的section需要进行多少次map,并在每遍历完一个node后或对其pageblock_flags进行初始化或对其section_mem_map建立虚拟映射表

    sparse_early_usemaps_alloc_node函数

    内存基址和内存地址(内存管理12物理地址空间的有效性检测)(10)

    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函数

    内存基址和内存地址(内存管理12物理地址空间的有效性检测)(11)

    sparse_mem_map_populate实现

    • 第302行:通过页框号找到对应物理地址

    • 第304行:根据页框数量偏移得到结尾页框地址

    • 第306行:建立映射关系的实际函数,实现↓

    vmemmap_populate函数

    内存基址和内存地址(内存管理12物理地址空间的有效性检测)(12)

    vmemmap_populate实现(1)

    内存基址和内存地址(内存管理12物理地址空间的有效性检测)(13)

    vmemmap_populate实现(2)

    • 本文的重点在于Linux内核是怎么对mem_section中的section_mem_map和pageblock_flags初始化的。所以这个函数暂不分析,因为实在太晚了,该睡觉了~ 其实有一部分在前面分析过。

    ,