在上一篇文章中,主要从MMU的映射规则角度对内存管理进行了一些分析,经过一些初步学习(未包括LPAE方式)发现不同版本ARM对应的MMU映射和工作规则是兼容的(错了请拍砖),对于差异性就需要通过阅读对应的官方提供的芯片手冊(如多核cortex-a7手冊目前最新版本是r0p5,文件名是DDI0464F_cortex_a7_mpcore_r0p5_trm.pdf)来深入学习了。
在本篇中,侧重于分析内核是如何建立虚拟地址页面以及页面的使用分布情况,通过这种直观的方式来窥视内核的内存管理机制。还是基于上一篇所提及的软硬件平台进行分析,且仅针对内核启动后未进行任何显性的用户操作情况下的内核页面情况以便于排除干扰。按老规矩文章不会涉及太多具体代码,要了解内核内存管理细节还是要深入阅读代码的。另外用户空间进程的内存管理不在本文的分析范围内。
先回顾上一篇中提到的关于硬件平台物理地址分布的情况:
再看一下内核启动后的虚拟内存配置情况:
分析:
硬件一共2G物理内存,内核的虚拟内存配置方式是1G内核空间、3G用户空间方式,低端内存760M(貌似有gap存在,实际上用不了这么多),高端内存约1G多主要通过vmalloc的地址空间进行访问。其它各项含义由于网上有太多的说明因此不在本文中展开说明。
内核页表项目录(swapper_pg_dir,通过占用16K来管理4G空间)所在虚拟地址首地址是0xc0004000,它所存储的信息是进行内核页面分布情况分析的重要依据;而内核初始建立内核页面的主函数是paging_init,再由它衍生出多个分支建立不同层次(layout)的页面。在paging_init及其不同分支的关键函数中打印调试信息,就能帮助建立起一张完整的内核页表分布图:
在上述内核页面分布图中,各列所表示的含义是:
分析:
1、内核页表目录中实际使用的页表项是从0xc0006fc0到0xc0007ff8,其间有未使用的页表项(如0xc0007ca8到0xc0007ff0)用于临时性分配;
2、虚拟内存的分配并不一定按页表项所能管理的最大空间进行,如modules这一层次类型就存在四个分别占用51/17/3/2个小尺寸页面的虚拟内存区域;
3、页表项0xc0007b60到0xc0007bd8(32M空间)存在由lowmem和dmamem先后两次重复映射的情况,这个应该与dma操作需要线性地址方式有关;
4、未显示物理地址的项主要是在相关打印函数中没有加入对应的打印项,如果使用正确的方式应该也是可以获取到的;
接下来,基于上面的信息来进行一个虚拟和物理地址转换的实例分析。我们针对modules(0xbf000000到0xbfe00000)的三个页面进行分析,对应的虚拟地址首地址是0xbf000000、0xbf001000、0xbf002000,相关的计算规则参照上一篇中MMU的介绍。
分析:
虚拟页面地址连续而物理页面地址不连续,进一步打印虚拟和物理地址的内容发现是相同的,而且内容也确实能对应上模块文件中的内容;
在本篇中,侧重于分析内核是如何建立虚拟地址页面以及页面的使用分布情况,通过这种直观的方式来窥视内核的内存管理机制。还是基于上一篇所提及的软硬件平台进行分析,且仅针对内核启动后未进行任何显性的用户操作情况下的内核页面情况以便于排除干扰。按老规矩文章不会涉及太多具体代码,要了解内核内存管理细节还是要深入阅读代码的。另外用户空间进程的内存管理不在本文的分析范围内。
先回顾上一篇中提到的关于硬件平台物理地址分布的情况:
SRAM 0x00000000---0x0000BFFF
IO 0x01000000---0x01EFFFFF
DRAM 0x40000000---0xBFFFFFFF
BROM 0xFFFF0000---0xFFFF8FFF
再看一下内核启动后的虚拟内存配置情况:
Memory: 2048MB = 2048MB total
Memory: 2016448k/2016448k available, 80704k reserved, 1318912K highmem
Virtual kernel memory layout:
vector : 0xffff0000 - 0xffff1000 ( 4 kB)
fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB)
vmalloc : 0xf0000000 - 0xff000000 ( 240 MB)
lowmem : 0xc0000000 - 0xef800000 ( 760 MB)
pkmap : 0xbfe00000 - 0xc0000000 ( 2 MB)
modules : 0xbf000000 - 0xbfe00000 ( 14 MB)
.text : 0xc0008000 - 0xc094ff4c (9504 kB)
.init : 0xc0950000 - 0xc09bc4c0 ( 434 kB)
.data : 0xc09be000 - 0xc0ae5760 (1182 kB)
.bss : 0xc0ae5760 - 0xc0bd194c ( 945 kB)
分析:
硬件一共2G物理内存,内核的虚拟内存配置方式是1G内核空间、3G用户空间方式,低端内存760M(貌似有gap存在,实际上用不了这么多),高端内存约1G多主要通过vmalloc的地址空间进行访问。其它各项含义由于网上有太多的说明因此不在本文中展开说明。
内核页表项目录(swapper_pg_dir,通过占用16K来管理4G空间)所在虚拟地址首地址是0xc0004000,它所存储的信息是进行内核页面分布情况分析的重要依据;而内核初始建立内核页面的主函数是paging_init,再由它衍生出多个分支建立不同层次(layout)的页面。在paging_init及其不同分支的关键函数中打印调试信息,就能帮助建立起一张完整的内核页表分布图:
在上述内核页面分布图中,各列所表示的含义是:
index: 虚拟地址区域索引(升序排列)。索引具有两级,第一级表示虚拟地址区域是连续的,如果存在第二级则表示虚拟地址区域是不连续的;
page table: 虚拟地址对应页面类型。是段页、小尺寸(4K)页、未使用页;
pages: 虚拟地址区域页面数量;
capacity: 虚拟地址区域空间大小;
pmd: 虚拟地址区域页面在页表目录中位置;
va_start: 虚拟地址区域起始地址;
pa_start: 虚拟地址区域对应物理地址起始地址;
layout(order): 虚拟地址层次类型(虚拟地址区域创建顺序)
call stack: 虚拟地址区域创建的函数调用栈
分析:
1、内核页表目录中实际使用的页表项是从0xc0006fc0到0xc0007ff8,其间有未使用的页表项(如0xc0007ca8到0xc0007ff0)用于临时性分配;
2、虚拟内存的分配并不一定按页表项所能管理的最大空间进行,如modules这一层次类型就存在四个分别占用51/17/3/2个小尺寸页面的虚拟内存区域;
3、页表项0xc0007b60到0xc0007bd8(32M空间)存在由lowmem和dmamem先后两次重复映射的情况,这个应该与dma操作需要线性地址方式有关;
4、未显示物理地址的项主要是在相关打印函数中没有加入对应的打印项,如果使用正确的方式应该也是可以获取到的;
接下来,基于上面的信息来进行一个虚拟和物理地址转换的实例分析。我们针对modules(0xbf000000到0xbfe00000)的三个页面进行分析,对应的虚拟地址首地址是0xbf000000、0xbf001000、0xbf002000,相关的计算规则参照上一篇中MMU的介绍。
page1==>
va: 0xbf000000
L1_off(12bit): (va & ~0x000fffff) >> 20 = 0xbf0
=== Coarse page table ===
L2_off(8bit): (va & ~0xfff00fff) >> 12 = 0
Page_off(12bit): (va & ~0xfffff000) >> 0 = 0
=== Fine page table ===
L2_off(10bit): (va & ~0xfff003ff) >> 12 = 0
Page_off(10bit): (va & ~0xfffffc00) >> 0 = 0
pgd_base: 0xc0004000
pgd_size: 0x00004000
pge_idx: L1_off << 2 = 0x2fc0
pge: [pgd_base + pge_idx] = 0x6c6a9811 ==> Coarse page table
pmd_base(22bit): pge & ~0x000003ff = 0x6c6a9800
pmd_size: 0x00000400
pme_idx: L2_off << 2 = 0
pme: [pmd_base + pme_idx] = 0x6c95a45e ==> Small page(4kb)
pte(20bit): pme & ~0x00000fff = 0x6c95a000
pa: pte + Page_off = 0x6c95a000
page2==>
va: 0xbf001000
(计算过程略)
pa: 0x6c88d000
page3==>
va: 0xbf002000
(计算过程略)
pa: 0x6d3b2000
分析:
虚拟页面地址连续而物理页面地址不连续,进一步打印虚拟和物理地址的内容发现是相同的,而且内容也确实能对应上模块文件中的内容;