i386CPU中的页式内存管理的基本思路是:通过页面目录和页面表分两个层次实现从线性地址到物理地址的映射。
Linux内核的映射机制设计成三层,在页面目录和页面表中间增设了一层“中间目录”。在代码中,页面目录称为PGD,中间目录称为PMD,而页面表则称为PT。PT中的表项则称为PTE(Page
Table Entry)。
对于CPU发出的线性地址,虚拟的Linux内存管理单元分如下四步完成从线性地址到物理地址的映射:
(1)用线性地址中最高的那一位段作为下标在PGD中找到相应的表项,该表项指向相应的中间目录PMD。
(2)用线性地址的第二个位段作为下标在此PMD中找到相应的表项,该表项指向相应页面表。
(3)用线性地址的第三个位段作为下标在此页面表中找到相应的表项PTE,该表项中存放的就是指向物理页面的指针。
(4)线性地址的最后位段作为物理页面内的相对位移量,将此位移量与目标物理页面的起始地址相加便得到相应的物理地址。
就以i386来说,CPU实际上不是按三层而是按两层的模型进行地址映射的。这就需要将虚拟的三层映射落实到具体的两层映射,跳过中间的PMD层次。另一方面,从Pentium
Pro开始,Intel引入了物理层地址扩充功能PAE,允许将地址宽度从32位提高到36位,并且在硬件上支持三层映射模型。这样,在Pentium
Pro及以后的CPU上,只要将CPU的内存管理设置成PAE模式,就能使虚拟内存的映射变成三层模式。
include/asm-i386/pgtable.h中
#if CONFIG_X86_PAE
# include
#else
# include
#endif
文件pgtable-2level.h中定义了三层映射中PGD和PMD的基本结构:
#define PGDIR_SHIFT 22
#define PTRS_PER_PGD 1024
#define PMD_SHIFT 22
#define PTRS_PER_PMD 1
#define PTRS_PER_PTE 1024
这里PGDIR_SHIFT表示线性地址中PGD下标位段的起始位置,文件中将其定义为22,也即是bit22(第23位)。由于PGD是线性地址中最高的位段,所以该位段是从第23位到第32位,一共是10位。
在pgtable.h中
#define PGDIR_SIZE (1UL <<
PGDIR_SHIFT)
这个表示PGD中每一个表项所代表的空间(并不是PGD本身所占的空间)大小是1<<22。同时,PTRS_PER_PGD表示每个PGD表中指针的个数为1024。在32位的系统中每个指针的大小为4个字节,所以PGD表的大小为4KB。
PMD_SHIFT也定义为22,与PGD_SHIFT相同,表示PMD位段长度为0,一个PMD表项所表示的空间与PGD表项所代表的空间一样大的。而PMD表中指针的个数PTRS_PER_PMD则定义为1,表示每个PMD表中只有一个表项。
32位地址意味着4G字节的虚拟空间,Linux内核将这4G字节的空间分成两部分。将最高的1G字节(从虚地址0xC0000000至0xFFFFFFFF),用于内核本身,称为“系统空间”。而将较低的3G字节(从虚地址0x0至0xBFFFFFFF),用作各个进程的“用户空间”。
虽然系统空间占据了每个虚拟空间中最高的1G字节,在无力的内存中却总是从最低的地址0开始。所以,对于内核来说,其地址的映射是很简单的线性映射,0xC0000000就是两者之间的位移量。
在page.h中:
#define __PAGE_OFFSET (0xC0000000)
#define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET)
#define __pa(x) ((unsigned long)(x) - PAGE_OFFSET)
#define __va(x) ((void *)((unsigned long)(x) +
PAGE_OFFSET))
也就是说,对于系统空间而言,给定一个虚地址x,其物理地址是从x中减去PAGE_OFFSET;相应地,给定一个物理地址x,其虚地址是x+PAGE_OFFSET。
同时,PAGE_OFFSET也代表着用户空间的上限,所以常数TASK_SIZE就是通过它定义的
processor.h
#define TASK_SIZE (PAGE_OFFSET)
全局段描述表GDT的容量:
每个进程在GDT中需要两个表项,一个是局部段描述表LDT,另一个是任务状态表TSS。段寄存器中用作GDT表下标的位段宽度是13位,所以GDT中可以有8192个描述项。除了一些系统的开销(例如GDT中的第2项和第3项分别用于内核的代码段和数据段,第4项和第5项永远用于当前进程的代码段和数据段,第1项永远是0,等等)以外,尚有8180个表项可以使用,所以理论上系统中最大的进程数量是4090。