本文kernel代码分析基于以下
1.linux-4.14.159
2.64bit代码处理逻辑
linux内存管理非常复杂及庞大,这节我们看下内核虚拟内存布局,理解内核内存布局对认识内存管理至关重要,我们从源码来进行解析,这样通过理解记忆的话比较牢固。
在linux系统中,内核的虚拟地址空间对所有进程共享的,然而每一个进程存在自己的用户虚拟地址空间和共享的内核虚拟地址空间,此节我们仅研究内核虚拟地址空间。
arm v8架构可以支持32~48位的物理寻址,最大可以寻找256TB的物理地址空间,64位支持64位寻址,为什么最大48位呢?因为48位对目前的应用已经够用,就无需进行64位的物理寻址,太大势必会增加硬件的设计复杂度。
针对arm64的硬件体系结构,如果page大小位4KB,使用3级或4级转换表,内核虚拟地址空间支持39位(512G)或48位(256TB)的寻址空间。
先看地址空间大小:
arch\arm64\include\asm\memory.h
#define VA_BITS (CONFIG_ARM64_VA_BITS)
//.config中查看CONFIG_ARM64_VA_BITS=39,
VA_BITS虚拟地址宽度,最大支持48位,不同系统看具体配置的值。如我这边config中配置的时39位,即64位系统配置为39位,值为0x7F FFFF FFFF,这个换算过来即位512G。
0x0000_0000_0000_0000~0x0000_007f_ffff_ffff 512GB user
0xffff_ff80_0000_0000~0xffff_ffff_ffff_ffff 512GB kernel
如之前阐述,linux在arm架构上虚拟地址分位用户虚拟地址空间和内核虚拟地址空间这两个空间,如上每个空间支持512GB寻址。
内核虚拟地址空间起始地址
#define VA_START (UL(0xffffffffffffffff) - \
(UL(1) << VA_BITS) + 1)
//展开0xFFFF_FFFF_FFFF_FFFF-0x0000_0080_0000_0000+1=0xFFFF_FF80_0000_ 0000
//也就是说kernel虚拟地址空间起始地址为0xFFFF_FF80_0000_ 0000
内核线性映射的起始地址
#define PAGE_OFFSET (UL(0xffffffffffffffff) - \
(UL(1) << (VA_BITS - 1)) + 1)
///计算下来为0xFFFF_FFC0_0000_ 0000
KASAN和MODULE区域
KASAN(KernelAddressSANitizer)是一个动态检测内存错误的工具,主要解决use-after-free和out-of-bounds问题,其原理是利用额外的内存标记可用内存的状态。这部分额外的内存被称作shadow memory(影子区),KASAN将1/8的内存用作shadow memory,详细的内容请百度了解。
MODULE:是内核模块使用的虚拟地址空间,其大小为128MB
/*KIMAGE_VADDR - the virtual address of the start of the kernel image*/
#define KIMAGE_VADDR (MODULES_END)
#define MODULES_END (MODULES_VADDR + MODULES_VSIZE)
#define MODULES_VADDR (VA_START + KASAN_SHADOW_SIZE)
// 0xFFFF_FF90 0000 0000
#define MODULES_VSIZE (SZ_128M)
#ifdef CONFIG_KASAN
#define KASAN_SHADOW_SIZE (UL(1) << (VA_BITS - 3))
// 0x10_0000_0000 :64G
#define KASAN_THREAD_SHIFT 1
#else
我们可以看到MODULES_VADDR ,等于VA_START + KASAN_SHADOW_SIZE,而KASAN_SHADOW_SIZE计算下来为64G,
因此MODULES_VADDR 地址为 0xFFFF_FF90 0000 0000
MODULES_END 地址为0xFFFF_FF90_0800_0000
///已确认的内存布局如下:
0xFFFF_FF80_0000_0000 ------ VA_START
Kasan大小64G
0xFFFF_FF90_0000_0000 ------ MODULES_VADDR
MODULE大小为128M
0xFFFF_FF90_0800_0000 ------ MODULES_END
64G为512G的1/8,因此符合kasan的定义。
再看VMALLOC区域
此区域为vmalloc函数使用的虚拟地址空间,注意因为linux kernel更新很快,笔者研究的代码kernel image映射区域已经从从原来的线性映射区域搬移到了VMALLOC区域
#define PUD_SIZE (_AC(1, UL) << PUD_SHIFT)
#if CONFIG_PGTABLE_LEVELS > 3
#define PUD_SHIFT ARM64_HW_PGTABLE_LEVEL_SHIFT(1)
#endif
page_SHIFT=12 //(4K)
#define SZ_64K 0x00010000
#define ARM64_HW_PGTABLE_L