日期:2015年10月30日
分页机制
Linux(x64CPU)使用基于分页机制的虚拟内存。每个进程有256TB(48位)的虚拟地址空间。基于分页机制,这256TB地址空间的一些部分 被映射了物理内存,一些部分什么也没有映射。程序中使用的都是256TB地址空间中的虚拟地址。而访问物理内存,需要使用物理地址。
物 理地址(physical address):放在寻址总线上的地址。放在寻址总线上,如果是读,电路根据这个地址每位的值就将相应地址的物理内存中的数据放到数据总线中传输。如果 是写,电路根据这个地址每位的值就将相应地址的物理内存中放入数据总线上的内容。物理内存是以字节(8位)为单位编址与寻址的。
虚拟地址(virtual address): 256TB虚拟地址空间中的地址,程序中使用的都是虚拟地址。
如 果CPU相关寄存器中的分页标志位被设置,那么执行内存操作的机器指令时,CPU会自动根据相应转换结构(PML4,PDPT,PD,PT)中的信息,把 虚拟地址转换成物理地址,完成该指令。比如"mov eax,dword ptr[13FF3904Ch]",就是把地址13FF3904Ch处的值赋给寄存器的汇编代码,13FF3904Ch这个地址就是虚拟地址。CPU在执 行这行代码时,发现寄存器中的分页标志位已经被设定,就自动完成虚拟地址到物理地址的转换,使用物理地址取出值,完成指令。对于x86-64CPU 来说,寄存器CR0的第31位是分页标志位,为1表示使用分页,为0表示不使用分页。对于初始化之后的 Linux 我们通过qemu monitor 观察 CR0,发现第31位的值为1。表明Linux是使用分页的。有多个寄存器中的控制位与分页有关,具体细节可以参考Intel软件开发手册。
x86-64 CPU的实际虚拟地址大小为48位(64位中的低48位,高16位不用于地址转换),可寻址256TB地址空间。x86-64 CPU的实际物理地址大小,不同的CPU不相同,但最大不超过52位。可以通过机器指令cpuid来查询该CPU的实际虚拟地址长度和实际物理地址长度。 例如,我查询了两台计算机,虚拟地址的大小两台计算机都为0x30(16进制30就是十进制48),物理地址的大小一台计算机为0x24(16进制24就 是十进制36,36位可寻址64GB),另一台计算机为0x27(16进制27就是十进制39,39位可寻址512GB)。为了后面的叙述方便,我们把 CPU实际物理地址的位数叫做M,比如,对于物理地址大小为36位的CPU,M就是36。
使用了分页机制之后,256TB的地址空 间被分成了固定大小的页(有三种大小,4KB,2MB,1GB),每一页或者被映射到物理内存,或者没有映射任何东西。对于一般程序来说,256TB的地 址空间,只有一小部分(沧海一粟)映射了物理内存,大片大片的部分是没有映射任何东西。物理内存也被分页,来映射地址空间。对于x86-64,页的大小有 三种,分别是4KB,2MB,1GB。CPU用来把虚拟地址转换成物理地址的信息存放在叫做"page map level 4"," page directory pointer","pagedirectory"(页目录),"pagetable"(页表)的结构里。每个进程都有自己的一套 PML4,PDPT,PD,PT结构。一个进程中的虚拟地址,最多需要四级转换,来得到对应的物理地址。
物理内存分页,一个物理页 的大小为4KB,第0个物理页从物理地址 0x0000000000000000处开始,大小为4KB(0x1000B),第1个物理页从物理地址 0x0000000000001000 处开始。第2页从物理地址0x0000000000002000处开始。由于页的大小是4KB,所以只需要64位的地址中的M-12bit(低12bit 之后)来寻址物理页。
"pagetable"(页表),一个页表的大小为4KB,放在一个物理页中。由512个8字节的PTE(页表项)组成。页表项的大小为8个字节(64 位),所以一个页表中有512个页表项。页表中的每一项的内容(每项8个字节,64位)低12位之后的M-12位用来放一个物理页的物理地址,低 12bit放着一些标志。
"pagedirectory"(页目录),一个页目录大小为4KB,放在一个物理页中。由512个8字节的PDE(页目录项)组成。页目录项的大小为8 个字节(64位),所以一个页目录中有512个页目录项。页目录中的每一项的内容(每项8个字节,64位)低12位之后的M-12位用来放一个页表(页表 放在一个物理页中)的物理地址,低12bit放着一些标志。
"pagedirectory pointer"表,一个page directorypointer表大小为4KB,放在一个物理页中。由512个8字节的PDPTE项组成。PDPTE项的大小为8个字节(64位),所 以一个pagedirectory pointer表中有512个PDPTE。page directorypointer表中的每一项的内容(每项8个字节,64位)低12位之后的M-12位用来放一个页目录(页目录放在一个物理页中)的物 理地址,低12bit放着一些标志。
"page maplevel 4"表,一个page map level4表大小为4KB,放在一个物理页中。由512个8字节的PML4E项组成。PML4E项的大小为8个字节(64位),所以一个pagemap level 4表中有512个PML4E。page map level4表中的每一项的内容(每项8个字节,64位)低12位之后的M-12位用来放一个page directory pointer表(pagedirectory pointer表放在一个物理页中)的物理地址,低12bit放着一些标志。
对于x86-64系统,"page map level 4"表的物理地址放在CPU的CR3寄存器中。
CPU把虚拟地址转换成物理地址:
一 个虚拟地址,大小8个字节(64位,实际只使用低48位),包含着找到物理地址的信息,分为5个部分:第39位到第47位这9位(最高9位) 是"pagemap level 4"表中的索引,第30位到第38位这9位是"page directorypointer"表中的索引,第21位到第29位这9位是页目录中的索引,第12位到第20位这9位是页表中的索引,第0位到第11位 这12位(低12位)是页内偏移。对于一个要转换成物理地址的虚拟地址,CPU首先根据CR3中的值,找到"pagemap level4"表所在的物理页,然后根据虚拟地址的第39位到第47位这9位(最高9位)的值作为索引,找到相应的PML4E项,PML4E项中有这个虚 拟地址所对应的"pagedirectory pointer"表的物理地址。有了"page directorypointer"表的物理地址,根据虚拟地址的第30位到第38位这9位的值作为索引,找到该"page directorypointer"表中相应的PDPTE项,PDPTE项中有这个虚拟地址所对应的页目录的物理地址。有了页目录的物理地址,根据虚拟地 址的第21位到第29位这9位的值作为索引,找到该页目录中相应的页目录项,页目录项中有这个虚拟地址所对应的页表的物理地址。有了页表的物理地址,根据 虚拟地址的第12位到第20位这9位的值作为索引,找到该页表中相应的页表项,页表项中有这个虚拟地址所对应的物理页的物理地址。最后用虚拟地址的最低 12位,也就是页内偏移,加上这个物理页的物理地址,就得到了该虚拟地址所对应的物理地址。
一个"page maplevel 4"表有512项,虚拟地址从48位向低走的9位刚好可以索引512项(2的9次方等于512),一个"pagedirectorypointer"表有 512项,虚拟地址接下来的9位刚好索引512项。一个页目录有512项,虚拟地址接下来的9位刚好索引512项。一个页表有512项,虚拟地址接下来的 9位刚好索引512项。虚拟地址最低的12位(2的12次方等于4096),作为页内偏移,刚好可以索引4KB,也就是一个物理页中的每个字节。
一 个虚拟地址转换成物理地址的计算过程就是,处理器通过CR3找到当前"page map level4"表所在物理页,取虚拟地址从48位向低走的9位,然后把这9位右移3位(因为每个PML4E项8个字节长,右移3位相当于乘8)得到在该页 中的地址,取出该地址处的PML4E(8个字节),就找到了该虚拟地址对应"pagedirectorypointer"表所在物理页,然后同样方法依次 找出该虚拟地址对应的页目录所在物理页,该虚拟地址对应的页表所在物理页,该虚拟地址对应的物理页的物理地址,最后将虚拟地址对应的物理页的物理地址加上 12位的页内偏移得到了物理地址。
48位的一个指针,可以寻址范围0x000000000000-0xFFFFFFFFFFFF,256TB大小。也就是说一个48位的指针可以寻址整个256TB地址空间的每一个字节。
一个页表项负责4KB的地址空间和物理内存的映射,一个页表512项,也就是负责512*4KB=2MB的地址空间的映射。
一个页目录项,对应一个页表。一个页目录有512项,也就对应着512个页表,每个页表负责2MB地址空间的映射,512个页表负责512*2MB=1GB的地址空间映射。
一个PDPTE项,对应一个页目录。一个"page directorypointer"表有512项,也就对应着512个页目录,每个页目录负责1GB地址空间的映射。512个页目录负责512*1GB=512GB的地址空间映射。
一 个"page map level 4"表项,对应一个"page directory pointer"表。一个"page maplevel 4"表有512项,也就对应着512个"page directory pointer"表,每个"page directorypointer"表负责512GB地址空间的映射。512个"page directorypointer"表负责512*512GB=256TB的地址空间映射。
一个进程有一个"page map level4"表。所以以页为单位,一套PML4,PDPT,PD,PT结构可以保证256TB的地址空间中的每页和物理内存的映射。
一 个虚拟地址在转换过程中,如果发现对应的PDPTE项的PS位为0则继续之后的转换步骤,如果发现对应的PDPTE项的PS位为1,则本PDPTE项中的 物理地址,就是该虚拟地址对应的一个大小为1GB物理页的地址,该虚拟地址之后的30位为这个1GB物理页的页内偏移,就可以得到该虚拟地址对应的物理地 址。
一个虚拟地址在转换过程中,如果发现对应的页目录项的PS位为0则继续之后的转换步骤,如果发现对应的页目录项的PS位为1, 则本页目录项中的物理地址,就是该虚拟地址对应的一个大小为2MB物理页的地址,该虚拟地址之后的21位为这个2MB物理页的页内偏移,就可以得到该虚拟 地址对应的物理地址。
虚拟地址48位,寻址256TB。48位地址:9bit+9bit+9bit+9bit+12bit。
每一级表的大小都为4KB。表项大小8B(64位),所以每个表512项。
4级转换:CR3-> "page map level 4"表-> "page directorypointer"表-> 页目录-> 页表-> 得到物理地址。
一个PTE项对应4KB地址范围。一张PT表对应512*4KB=2MB地址范围。
一个PDE项对应2MB地址范围。一张PD表对应512*2MB=1GB地址范围。
一个PDPTE项对应1GB地址范围。一张PDPT表对应512*1GB=512GB地址范围。
一个PML4E项对应512GB地址范围。一张PML4表对应512*512GB=256TB地址范围。
页大小有3种,4KB,2MB,1GB。
每 个进程都有自己的256TB地址空间,"0000000000000000 - 00007fffffffffff" 和"ffff800000000000 - ffffffffffffffff" 。因为CPU会忽略高16位,所以相当于"000000000000 - 7fffffffffff" 和 "800000000000 -ffffffffffff"。
因为CPU有特殊规定,被忽略的高16位的每一 位必须等于第47位的值(并会做检查,不符合规定将引发异常)。"000000000000 -7fffffffffff"的第47位是0,所以高16位也必须都为0。"800000000000 -ffffffffffff"的第47位是1,所以高16位也必须都为1。
每个进程都有自己的256TB地址空间,通过每个进程自己的一套 PML4,PDPT,PD,PT结构来实现。由于每个进程有自己的一套PML4,PDPT,PD,PT结构,所以每个进程的地址空间映射的物理内存是不一 样的。两个进程的同一个虚拟地址处(如果都有物理内存映射)的值一般是不同的,因为他们往往对应不同的物理页。
256TB地址空间 中低128TB,0x000000000000-0x7fffffffffff是用户地址空间,256TB地址空间中高 128TB,0x800000000000-0xffffffffffff是系统地址空间。访问系统地址空间需要程序有ring0的权限。
未完待续...
微博:http://weibo.com/ddqqppb
邮箱:thejiurl@163.com
QQ:6291898
欢迎交流。