1. ARM64 Linux内核虚拟地址空间划分
因为目前应用没有那么大的内存需求,所以ARM64处理器不支持完全的64位虚拟地址,实际支持情况如下:
- 虚拟地址的最大宽度是48位(256TB),如下图所示。内核虚拟地址在64位地址空间的顶部,高16位全为1,范围是[0xFFFF 0000 0000 0000, 0xFFFF FFFF FFFF FFFF];用户虚拟地址在64位地址空间的底部,高16位是全0,范围是[0x0000 0000 0000 0000, 0x0000 FFFF FFFF FFFF],高16位是全1或全0的地址称为规范的地址,两者之间是不规范的地址,不允许使用。
ARM64 Linux内核中没有高端内存,因为48位的寻址空间已经足够大(参考:Linux内核中什么是高端内存?-CSDN博客)。
在ARM64架构的Linux内核中,内核虚拟地址和用户虚拟地址的宽度相同。每个进程的用户空间是完全独立、互不相干的,用户进程各自有不同的页表。而内核空间是由Linux内核负责映射,它并不会跟着进程改变,是固定的。内核空间的虚拟地址到物理地址映射是被所有进程共享的,内核的虚拟空间独立于其他程序。同一个线程组的用户线程共享用户虚拟地址空间,内核线程没有用户虚拟空间地址。
2. 内核虚拟空间布局
如下是NXP i.MX8MP Linux 5.15.X内核的内存分布图:
如上信息的输出是在mem_init()函数中实现的。注意:该信息已经在Linux 4.16.x内核中删除,请在Linux 5.15.x的内核中应用如下patch。
下面对i.MX8MP Linux 5.15.x内核的内存分布进行一个详解:
- PAGE_OFFSET,表示物理内存在内核空间里做线性映射(linear mapping)的起始地址,在i.MX8MP Linux 5.15.x中该值被定义为:0xFFFF 0000 0000 0000。Linux内核在初始化时会对物理内存全部做一次线性映射,将它们映射到内核空间的虚拟地址。
- PHYS_OFFSET,表示物理内存在地址空间中的偏移量,有不少SoC在设计时就没有把物理内存的起始地址固定在0x0地址处。在i.MX8MP Linux 5.15.x中被定义为:0x4000 0000。
- KIMAGE_VADDR,表示内核映像文件映射到内核空间的起始虚拟地址。它的值等于MODULES_END的值,MODULES_END表示模块区域的虚拟地址的结束地址。在i.MX8MP Linux 5.15.x内核中KIMAGE_VADDR的值为:0xFFFF 8000 0800 0000,它的定义如下:
- 代码(.text)段: _text和_etext为代码段的起始与结束地址,包含了编译后的内核代码。
- init段:__init_begin和__init_end分别为init段的起始与结束地址,包含了大部分模块初始化的数据。
- 数据(.data)段:_sdata和_edata分别为数据段的起始和结束地址,保存了内核的大部分变量。
- bss段:__bss_start和__bss_stop分别为bss段的开始和结束地址,包含了未初始化的或者初始化为0的全局变量和静态变量。
最后我们对i.MX8MP Linux 5.15.x内核虚拟内存布局做一个总结,具体如下所示: