系统32位和64位应该指的是地址线位数,但是不是物理地址总线地址,而应该是虚拟地址。对于Linux而言,就是线性地址宽度(没有使用分段机制,以及开启分页机制)。
64位的内核不是意味着就有64根地址线(虚拟地址)。因为没必要,实际不需要那么大内存。假设你有38位地址线(虚拟地址),可以寻址到2048G的内存,也按照3:1划分,那么内核态就有512G范围,你的512G物理内存可以一次性的全部映射到内核空间,根本不需要高端内存。
Intel 32位有四种,32-bit(4KB页大小和4MB页大小)和PAE(4KB页大小和2MB页大小)。
然而, 大型服务器需要大于4GB的RAM来同时运行数以钱计的进程, 所以必须扩展32位80x86架构所支持的RAM容量.
Intel通过在它的处理器上把管脚数从32增加到36满足这样的需要, 从Pentinum Pro开始, Intel所有处理器的寻址能力可达到2^36=64GB, 但是只有引入一种新的分页机制才能把32位现行地址转换为36位物理地址才能使用所增加的物理地址.
从Pentinum Pro处理器开始, Intel引入一种叫做物理地址扩展(Physical Address Extension, PAE)的机制.
从Pentium模型开始,80x86微处理器引入了扩展分页(externded paging),也叫页大小扩展[Page Size Extension], 它允许页框大小为4MB而不是4KB。扩展分页用于把大段连续的线性地址转换成相应的物理地址,在这种情况下,内核可以不用中间页表进行地址转换,从而节省内存并保留TLB项。
Linux没有采用4MB页大小
正如前面所述,通过设置页目录项的Page Size标志启用扩展分页功能。在这种情况下,分页单元把32位线性地址分成两个字段: Directory:最高10位。 Offfset:其余22位。
扩展分页和正常分页的页目录项基本相同,除了
- Page Size标志必须被设置。
- 20位物理地址字段只有最高10位是有意义的。这是因为每一个物理地址都是在以4MB为边界的地方开始的,故这个地址的最低22位为0。
通过设置cr4处理器寄存器的PSE标志能使扩展分页与常规分页共存
Intel为了支持PAE改变了分页机制
-
64GB的RAM被分成了2^24个页框,页表项的物理地址字段从20位扩展到了24位.因为PAE页表项必须包含12个标志位(4K页大小,偏移offset位数需要12位)和24个物理地址位(因为页框是2^24个,所以需要24位物理地址位),总数之和为36,页表项大小从32位扩展到了64位,结果,一个4KB的页表项包含512个表项而不是1024个表项
-
引入一个页目录指针表(Page Directory Pointer Table,PDPT)的页表新级别,它由4个64位表项组成.
-
cr3控制寄存器包含一个27位的页目录指针表(PDPT)基地址字段.因为PDPT存放在RAM的前4GB中,并在32字节(2^5)的倍数上对其, 因此27位足以表示这种表的基地址
-
当把线性地址映射到4KB的页时(页目录项中的PS标准清0), 32位线性地址将按照如下方式解释
当把现行地址映射到2MB的页时(页目录项中的PS标志置为1), 32位线性地址按照如下方式解释
总之, 一旦cr3被设置, 就可能寻址高达4GB RAM, 如果我们期望堆更多的RAM进行寻址, 就必须在cr3中放置一个新值, 或改变PDPT的内容.
但是PAE的主要问题是线性地址仍然是32位长, 这就需要内核黑客用同一线性地址映射不同的RAM区. 很显然, PAE并没有扩大进程的线性地址空间, 因为它只处理物理地址. 此外, 只有内核能够修改进程的页表, 所以在用户态下运行的程序不可能使用大于4GB的物理地址空间. 另一方面, PAE允许内核使用容量高达64GB的RAM, 从而显著的增加系统中的进程数目。
正常来说, 对于32位的系统两级页表已经足够了, 但是对于64位系统的计算机, 这远远不够.
首先假设一个大小为4KB的标准页.因为1KB覆盖210个地址的范围,4KB覆盖212个地址,所以offset字段需要12位.这样线性地址空间就剩下64-12=52位分配给页中间表Table和页目录表Directory.如果我们现在决定仅仅使用64位中的48位来寻址(这个限制其实已经足够了,2^48=256TB,即可达到256TB的寻址空间).剩下的48-12=36位被分配给Table和Directory字段.即使我们现在决定位两个字段各预留18位,那么每个进程的页目录和页表都包含218个项, 即超过256000个项.
基于这个原因, 所有64位处理器的硬件分页系统都使用了额外的分页级别.使用的级别取决于处理器的类型
注:ia64是intel的一门高端技术,不与x86_64系统兼容;IA-32e Paging机制下线性地址映射到4KB的页
当然,如果没有开启分页,线性地址就是物理地址。
不同架构的分页机制
对于不同的体系结构,Linux采用的四级页表目录的大小有所不同:对于i386而言,仅采用二级页表,即页上层目录和页中层目录长度为0;对于启用PAE的i386,采用了三级页表,即页上层目录长度为0;对于64位体系结构,可以采用三级或四级页表,具体选择由硬件决定。
对于没有启用物理地址扩展的32位系统,两级页表已经足够了。从本质上说Linux通过使“页上级目录”位和“页中间目录”位全为0,彻底取消了页上级目录和页中间目录字段。不过,页上级目录和页中间目录在指针序列中的位置被保留,以便同样的代码在32位系统和64位系统下都能使用。内核为页上级目录和页中间目录保留了一个位置,这是通过把它们的页目录项数设置为1,并把这两个目录项映射到页全局目录的一个合适的目录项而实现的。
启用了物理地址扩展的32 位系统使用了三级页表。Linux 的页全局目录对应80x86 的页目录指针表(PDPT),取消了页上级目录,页中间目录对应80x86的页目录,Linux的页表对应80x86的页表。
最终,64位系统使用三级还是四级分页取决于硬件对线性地址的位的划分。
线性地址转换成物理地址
地址转换过程有了上述的基本知识,就很好理解四级页表模式下如何将虚拟地址转化为逻辑地址了。基本过程如下:
-
从CR3寄存器中读取页目录所在物理页面的基址(即所谓的页目录基址),从线性地址的第一部分获取页目录项的索引,两者相加得到页目录项的物理地址。
-
第一次读取内存得到pgd_t结构的目录项,从中取出物理页基址取出(具体位数与平台相关,如果是32系统,则为20位),即页上级页目录的物理基地址。
-
从线性地址的第二部分中取出页上级目录项的索引,与页上级目录基地址相加得到页上级目录项的物理地址。
-
第二次读取内存得到pud_t结构的目录项,从中取出页中间目录的物理基地址。
-
从线性地址的第三部分中取出页中间目录项的索引,与页中间目录基址相加得到页中间目录项的物理地址。
-
第三次读取内存得到pmd_t结构的目录项,从中取出页表的物理基地址。
-
从线性地址的第四部分中取出页表项的索引,与页表基址相加得到页表项的物理地址。
-
第四次读取内存得到pte_t结构的目录项,从中取出物理页的基地址。
-
从线性地址的第五部分中取出物理页内偏移量,与物理页基址相加得到最终的物理地址。
-
第五次读取内存得到最终要访问的数据。
整个过程是比较机械的,每次转换先获取物理页基地址,再从线性地址中获取索引,合成物理地址后再访问内存。不管是页表还是要访问的数据都是以页为单 位存放在主存中的,因此每次访问内存时都要先获得基址,再通过索引(或偏移)在页内访问数据,因此可以将线性地址看作是若干个索引的集合。