- UID
- 1043
- 精华
- 积分
- 11692
- 威望
- 点
- 宅币
- 个
- 贡献
- 次
- 宅之契约
- 份
- 最后登录
- 1970-1-1
- 在线时间
- 小时
|
提前声明:本文按照本人对《AMD64架构程序员编程手册》和《Intel 64与IA-32架构程序员开发手册》的理解而撰写,没有对网上其他资料进行任何参考。本文出现的中文名词有一部分是本人强行翻译的,其中可能会出现和其他中文资料相同的名词,但可能意义不同,阅读时请不要混为一谈。
物理地址扩展(PAE,即Physical Address Extension)是1995年的时候,Intel在Pentium Pro处理器引入的新技术。而八年后的2003年,AMD64以PAE为跳板,发展出名为“长模式”的新处理器模式。
在解释PAE之前,我们先讲分页模式:
分页机制下最基本的寄存器就是CR3寄存器,这个寄存器存储页表的物理地址。所有的线性地址到物理地址的转换,都要走过CR3寄存器。CR3寄存器的意义,在不同分页模式下结构不同。但第3,4位的意义始终不变:
第3位为PWT位,意义是启用page write-through的缓存机制。当PWT位置位时,处理器启用page write-through缓存机制,当PWT位复位时,处理器启用write-back缓存机制。关于缓存机制,这比较复杂,这里暂且不提。缓存机制的详情请见AMD64架构程序员编程手册第二卷第七章。
第4位为PCD位,意义是禁用分页缓存机制(Page Cache Disable)。当PCD位置位时,处理器禁用分页缓存机制,当PCD位复位时,处理器启用分页缓存机制。
在Windows操作系统中,第3,4位均被复位,即启用分页缓存机制,并使用write-back缓存机制。
在传统32位模式下,有两种页表结构:第一种是一级页表结构(即4MB页面模式),第二种是二级页表结构(即4KB页面模式)。而常用的32位操作系统,比如Windows,就是使用了二级页表结构。
在传统32位模式下的CR3寄存器结构如下:
其中第3,4位为缓存设置位,高20位为页目录物理地址的高20位,其余位全部被处理器保留。用汇编语言来取得页目录物理地址的代码如下:
- mov eax,cr3
- and eax,0fffff000h
复制代码
其中eax寄存器为页目录物理地址。可以看得出来,页目录物理地址是按照4KB对齐的。
先谈谈二级页表,32位的线性地址被分为三个部分:页偏移(Page Offset,长度为12位),页表偏移(Page Table Offset,长度为10位)与页目录偏移(Page Directory Offset,长度为10位),CR3寄存器指向4KB大小的页目录,其中包含1024个4字节的页目录记录(PDE,即Page Directory Entry)。
页目录记录的结构如下:
第0位为P位,即Present。当P位置位时,表示该页目录存在,当P位复位时,表示该页目录不存在,可以理解为被Windows API中的VirtualProtect设置为PAGE_NOACCESS的页面。
第1位为R/W位,即Read/Write。当R/W位置位时,表示该页目录可读写,当R/W位复位时,表示该页目录只可读。
第2位为U/S位,即User/Supervisor。当U/S位置位时,表示该页目录允许Ring3内的程序访问,当U/S位复位时,表示该页目录不允许Ring3内的程序的访问。
第3,4位为PWT位和PCD位,意义已在前文中讲述。
第5位为A位,即Accessed。当处理器在转换线性地址至物理地址时,若途径该页目录,则将A位置位。处理器不会自动将此位复位。软件可以手动将此位复位来追踪系统对该页目录的访问频率。
第7位为PS位,即Page Size。当该位置位时,该页目录表示一个4MB的大页面。在二级分页结构中,该位必须复位。
第9位至第11位在超威半导体的处理器中被定义为AVL位,即Available。这3位不被使用,系统软件可以瞎JB用这3位;在英特尔的处理器中,这三位被定义为IGN位,即Ignored,同样不被使用。
第6,8位不作使用。
第12位至第31位保存页表物理地址的高20位。它指向4KB大小的页表,其中包含1024个4字节的页表记录(PTE,即Page Table Entry)。
页表记录的结构如下:
大部分内容和页目录记录的定义一致,下面解释一下尚未被解释的位:
第6位为D位,即Dirty。在处理器转换线性地址至物理地址时,若途径该页表记录,则自动将该位置位。软件可以通过对该位复位来追踪该页的写入频率。
第7位为PAT位,即Page Attribute Table。该位结合PWT位和PCD位选择分页缓存模式。
第8位为G位,即Global。当该位置位时,表示该页是全局页,CR3在切换时,或发生任务切换时,全局页的TLB缓存将不被刷新。
第12位至第31位保存物理页面的物理地址的高20位。物理页面的物理地址+页偏移=线性地址表示的物理地址。
大体来讲,二级页表下线性地址到物理地址的转换示意图如图所示:
再谈谈一级页表,32位的线性地址中,原本的页表偏移被并入页偏移。如此一来,页偏移长度由12位暴涨至22位,剩余的就是10位长的页目录偏移。此时CR3寄存器仍然指向4KB大小的页目录,其中仍然包含1024个4字节的页目录记录。
由于一级页表的特殊性,它允许将线性地址转换至至高40位的物理地址(实际上可以转换到多大的物理地址因为处理器的不同而不同,这需要通过cpuid指令查询)。使用一级页表需要启用大页面模式,即需要对CR4.PSE置位。
页目录记录的结构如下:
低12位和页表记录的低12位大致一样,只有第7位不同。
第7位是PS位,即Page Size。由于在一级页表下需要表示一个4MB的大页面。因此在一级分页结构中,该位必须置位。
而PAT位由第7位移动到第12位,意义不再解释。
第22位置第31位保存物理页面的物理地址的第22位置第31位,而第13位至第20位保存物理页面的物理地址的第32位第39位。同样的,物理页面的物理地址+页偏移=线性地址表示的物理地址。
大体来讲,一级页表下的线性地址到物理地址的转换示意图如图所示:
值得注意的是,二级页表和一级页表是可以同时使用的。
32位分页模式还有一种分页模式,叫PAE分页模式。在这种分页模式下,支持将32位的线性地址转换至至高52位长的物理地址(实际上可以转换到多大的物理地址因为处理器的不同而不同,这需要通过cpuid指令查询)。由于最早的支持PAE处理器支持36位长的物理地址,因此网上有误传称PAE可以支持64GB的内存,这也使得很多人认为开启了PAE后,32位操作系统就变成了“36位操作系统”,可以说是相当搞笑了。实际上,支持PAE的32位操作系统,完全可以使用处理器能支持的全部物理内存。只不过虚拟地址空间仍然是32位,在一条指令中允许访问的内存由当前的页表决定。
在PAE分页模式下,CR3寄存器结构发生变化,其意义也发生了变化,不再指向页目录,而是指向四个PDPTE寄存器(Page Directory Pointer Table Entry Register,即页目录指针表寄存器)。实际上页目录指针表寄存器并不是真正意义上的寄存器,而是8字节长的内存区域,“寄存器”的说法仅存在于Intel的手册上。因此所谓指向四个PDPTE寄存器的意思就是指向一个32字节长的PDPTE区。PDPTE也被称为PDPE,即页目录指针记录。此时CR3寄存器结构如下:
其中低3位被保留,第3,4位为PWT和PCD位,高27位指向PDPE区。
而线性地址被分为四个部分:页目录指针表偏移(长度为2位),页目录偏移(长度为9位),页表偏移(长度为9位),页偏移(长度为12位)。
每个页目录指针表记录的结构如下:
其中P,PWT,PCD,AVL位的意义不再解释,具体意义详见上文。
第12位至第51位表示页目录地址的高40位,意义相当于传统分页模式的CR3寄存器的指向。只不过物理地址长度由32位扩展至至多52位长。
在PAE分页模式下,页表结构有三级页表(4KB页面)和二级页表(2MB页面)两种。在启用了PAE的32位Windows操作系统中,使用了三级页表。与PDPE相似,PDE与PTE的每个项均为8字节长,因此每4KB内存可以放置512个记录项,线性地址用9位长的二进制数来指向特定的项。
要使用PAE模式,必须要对CR4.PAE置位。
在三级页表结构下,页目录记录的结构如下:
其中前文未提及的位是最高位NX位,该位表示Non-Execute属性。当该位置位时,页面的内存里的代码不可执行,当该位复位时,页面里的内存代码可执行。
与PDPE相似,PDE记录的第12位至51位表示页表地址的高40位。
在三级页表结构下,页表记录的结构如下:
其中第12位至第51位表示物理页的高40位。
与传统分页模式相似,PAE分页模式下,物理页面的物理地址+页偏移=线性地址表示的物理地址。
用图表示PAE分页模式的三级页表结构:
除了三级页表结构外,PAE模式还有二级页表结构。和传统分页模式的一级页表相似,在PAE的二级页表下,页偏移和页表偏移被合并,长度由12位暴涨至21位,因此页面大小也就从4KB暴涨至2MB。同样的,要使用二级页表结构,PDE.PS需要置位,但不需要对CR4.PSE置位。
PAE的二级页表结构下,PDE记录的结构如图所示:
其中每一项的意义均在前文中有解释,此处不再赘述。
和传统模式相似,PAE可以同时使用三级页表和二级页表。
AMD64架构核心——长模式分页。
长模式是AMD64架构的核心,它使用48位长的二进制数表示线性地址。而长模式的分页则是以PAE的分页模式作为跳板,将32位的线性地址扩展至48位,并转换成52位长的物理地址。其中页目录指针表(PDPE)偏移从2位增长至9位,并增加9位长的第四级映射页(Page Map Level 4 Entry, PML4E)偏移。64位的操作系统表面上说不支持PAE,但其实没必要强调,因为要启用长模式分页机制,就必须要启用PAE模式,否则就不能用52位长的物理地址。在64位的Windows中,使用长模式分页的四级页表结构。
尽管在AMD64架构下,线性地址起到作用的其实只有48位长,但仍然使用64位整数表示。64位整数表示的线性地址必须要符合canonical形式。即高16位(第48位至第63位)保持一致,要么每一位都是1,也就是0xFFFF,要么每一位都是0,也就是0x0000。如果使用的线性地址不符合这种形式,则按照引用该地址的段,处理器抛出#GP或#SS异常,不会产生#PF异常。
长模式下的CR3寄存器由32位扩展至64位,其中第3,4位仍然是与缓存相关的PWT与PCD位,而第12位置第51位则表示第四级映射页的物理地址的高40位。如图所示:
第四级映射页占4KB大小,记录了512条PML4E记录,PML4E记录的结构如下图所示:
其中大多标志位的意义都已在上文中做出解释。此处不再赘述。PML4记录的第12至第51位保存的是PDPE物理地址的高40位。
值得一提的是,MBZ和IGN在AMD64手册中均为“Must Be Zero”和“Ignored”的意思。
而PDPE和PDE,PTE的结构和前文中PAE分页模式下的PDPE,PDE,PTE结构大致一致。此处略过。
长模式下除了可以使用四级页表结构,还有三级页表结构和二级页表结构,分别对应2MB分页模式和1GB分页模式,与之相呼应的,使用时需要对PDE.PS和PDPE.PS置位,此外还要相应的设置第12位上的PAT位。
假设我们要建立一个页表,使得线性地址等于物理地址(即对等页表),并支持至多512GB的内存。我们需要为PML4E分配4KB的内存,为PDPE分配4KB的内存。如果用二级页表结构,那么页表的内存分配就到此为止。如果用三级页表,那么需要再为PDE分配2MB的内存。如果用四级页表,那么需要再为PTE分配1GB的内存。需要注意的是大多较老的处理器支持三级页表和四级页表(可以断言所有支持AMD64架构的处理器都支持三级页表和四级页表结构),而支持二级页表(即1GB分页)的就只有较新的处理器了。
同样的,在长模式下可以同时使用多种分页结构。
结语
写本文的起因是最近在做一个基于硬件虚拟化的OS核心,其中长模式分页原理成为了需要攻克的重点难题之一(虽然本文却很简单的代过了长模式分页的内容),此外也发现了很多对PAE产生误解的人以及相关的聊天记录,特此发了个帖。
最后在强调几点:
PAE模式不是把地址长度扩展到36位,线性地址仍然是32位,在一条指令内只能访问当前页表能触及的内存。PAE不过是扩展了所支持的物理地址长度至至多52位,具体多大需要参照cpuid的查询结果。
在AMD64架构下,物理地址只不过被扩充到至高52位。如果是在虚拟机中,物理地址至高也就只有48位。而线性地址的长度虽然有64位长,但高16位不过是为了符合canonical形式而存在的填充位,因此线性地址其实只有48位长。
2019-04-25补充:
当初在写这篇文章时本人基本上是完全针对超威半导体处理器而写,没有注意到英特尔对分页还有些乱七八糟的骚操作。这里给出的补充内容需要结合本人讲解分页缓存体系的帖子进行阅读:【处理器】简述AMD64体系的分页缓存体系
前文说过CR3寄存器的第3,4位的意义一直是PWT,PCD。然而,这个说法是错误的。在英特尔处理器的长模式分页(英特尔似乎把长模式叫IA-32e模式)中,如果对CR4.PCIDE进行置位,则CR3寄存器的低12位将用于表示PCID。
你问我PCID是干啥的?请去刚才给出的新链接。 |
|