Linux内核采用页式存储管理。虚拟地址空间划分成固定大小的“页面”,由CPU的MMU在运行时讲虚拟地址“映射”成某个物理内存页面的地址。
由于i386 CPU对地址先进行段式映射(必须的),然后才能进行页式映射(可选的)。
Linux让段式映射前后的地址一样。
对于用户空间内的虚拟地址0x08048368为例,进行说明。
第一. i386 CPU使用代码段寄存器CS的当前值来作为段式映射的“选择码”,也就是它在段描述表中的下标。
#define __KERNEL_CS 0x10
#define __KERNEL_DS 0x18
#define __USER_CS 0x23
#define __USER_DS 0x2B
我们的地址在进程的用户空间运行,内核在调度该进程进入运行时,把CS设置成__USER_CS,即0x23。index=4,GDT[4]中保存的是其对应的段描述项。
ENTRY(gdt_table)
.quad 0x0000000000000000 /* NULL descriptor */
.quad 0x0000000000000000 /* not used */
.quad 0x00cf9a000000ffff /* 0x10 kernel 4GB code at 0x00000000 */
.quad 0x00cf92000000ffff /* 0x18 kernel 4GB data at 0x00000000 */
.quad 0x00cffa000000ffff /* 0x23 user 4GB code at 0x00000000 */
.quad 0x00cff2000000ffff /* 0x2b user 4GB data at 0x00000000 */
.quad 0x0000000000000000 /* not used */
.quad 0x0000000000000000 /* not used */
GDT表项下标为4的值是0x00cffa000000ffff /* 0x23 user 4GB code at 0x00000000 */
将各项展开
可以看到段基址相同,虚地址到线性地址的映射保持不变。段式映射机制把地址0x08048368映射到了其自身,作为线性地址。
下面进行页式映射的过程
线性地址0x08048368按二进制展开
0000 1000 0000 0100 1000 0011 0110 1000