页表的概念中,还有几个页表结构需要了解,分别为进程页表和内核页表:
进程页表
进程的线性地址空间分为两个部分:
- 从0x00000000到0xbfffffff的线性地址,无论进程运行在用户态还是内核态,都可以寻址。
- 从0xc0000000到0xfffffff的线性地址,只有内核态的进程才能访问。
当进程运行在用户态时,它产生的线性地址小于0xc0000000,当进程运行在内核态时,它执行的是内核代码,所产生的地址大于等于0xc0000000,但是,在某些情况下,内核为了检索或存放数据必须访问用户态线性地址空间。
宏PAGE_OFFSET的值是0xc0000000。这就是线性地址空间中的偏移量,页是内核生存空间的开始之处。页全局目录的第一部分表项映射的线性地址小于0xc0000000,具体大小依赖于特定的进程。相反,剩余的表项对所有的进程来说都应该是相同的,它们等于主内核页全局目录的相应表项。
内核页表
内核维持着一组自己使用的页表,驻留在所谓的主内核页全局目录(master kernel Page Global Directory)中,系统初始化后,这组表还未被任何进程或任何内核线程直接使用,更准确的说,主内核页全局目录的最高目录项部分作为参考模型,为系统中每一个普通进程对应的页全局目录项提供参考模型。
内核初始化自己的页表需要两个阶段,内核映象刚刚被装入内存后,CPU仍然运行于实模式,所以分页功能没有被启用。
x86体系的处理器刚开始时只有20根地址线,寻址寄存器是16位。所以实模式是指寻址采用和8086相同的16位段和偏移量,最大寻址空间1MB1,最大分段64KB。可以使用32位指令。事实上,实模式将整个物理内存看成分段的区域,程序代码和数据位于不同区域,系统程序和用户程序并没有区别对待,而且每一个指针都是指向实际的物理地址。这虽然灵活但一方面给程序最大的权利,另一方面也带来了维护的困难,因为内存地址没有收到保护,所以可能因为内存被复写,导致系统崩溃。
为了克服这种问题,保护进程地址空间,处理器厂商开发出保护模式2。在保护模式中,寻址能力大大提高,而且因为内存不能直接被程序访问,需要通过逻辑地址到物理地址的转换去访问,所以物理地址对程序透明,每个进程都无法访问其他进程的地址,甚至也无法访问自己的虚地址。如果有访问,则会产生段错误。
Linux只有在刚刚启动时是实模式,然后就进入保护模式。
在第一个阶段,内核创建一个有限的地址空间,包括内核的代码段和数据段,初始页表和用于存放动态数据结构的一共128KB大小的空间。这个最小限度的地址空间仅能够将内核装入RAM并对其初始化核心数据结构。
第二个阶段,内核重复的利用剩余的RAM并适当地建立分页表。