前面学习了arm的两级页表机制,今天来学习下linux的虚拟内存布局。知道了总体的布局,我们再逐个去分析具体的建立过程。
在32位的系统上,内核使用第3GB~第4GB的线性地址空间,共1GB大小(当然,前面说了,这个比例是可以由用户配置的,我们以典型的3:1的比例来介绍)。内核将其中的前896MB与物理内存的0~896MB进行直接映射,即线性映射,并把它叫做低端内存。将剩余的128M线性地址空间作为访问高于896M的内存的一个窗口,把它叫做高端内存。
引入高端内存映射这样一个概念的主要原因就是我们所安装的内存大于1G时,内核的1G线性地址空间无法建立一个完全的直接映射来触及整个物理内存空间。
这128MB的高端内存划分如下(从high_memory开始):
上图是内核空间1G线性地址的布局,直接映射区为PAGE_OFFSET~PAGE_OFFSET+ 896MB,直接映射的物理地址末尾对应的线性地址保存在high_memory变量中。直接映射区后边有一个8MB的保护区,目的是用来"捕获"对内存的越界访问。然后是非连续内存区,范围从VMALLOC_START~VMALLOC_END,出于同样的原因,每个非连续内存区之间隔着4KB。永久内核映射区从PKMAP_BASE开始,大小为2MB。后边是固定映射区,范围是FIXADDR_START~FIXADDR_TOP。
上面的两个图是网上广为流传的图,实际上和我现在看到的5.2版本的内核有点出入。Pkmap区不是在vmalloc区之后,而是在3G内存下方2M处,vmalloc区之后空着4M的空间,接着就是fixmap区。
对于一个内存只有256M的板子的虚拟地址映射如下:
#define VMALLOC_END 0xff800000UL
static void * __initdata vmalloc_min =
(void *)(VMALLOC_END - (240 << 20) - VMALLOC_OFFSET);
新的内核代码限制了vmalloc区最小大小240M,从而也限制了低端内存的最大范围。vmalloc_min是为了满足vmalloc区最小大小240而计算出来的vmalloc起始地址的最小值,等于F0000000。
F0000000 - C0000000 = 768M,也就是说新版本的内核,低端内存最大只能是768M,不再是以前说的896M。
vmalloc_min变量在函数void __init adjust_lowmem_bounds(void)中使用,用于限制低端内存的最大值,high_memory全局变量也是在该函数初始化,从而确定了高端内存的起始地址,后面会介绍。
后面会详细介绍低端内存建立映射的过程,以及高端内存的三种访问方式:非连续内存区映射(vmalloc),永久内核映射(pkmap),临时内核映射(fixmap)。