我非常讨厌虚拟地址空间这个叫法,再配合上各种书上的矩形配图,更可恶的是物理内存的配图也常为矩形配图,这很容易让人定式的认为虚拟地址空间就是一个物理学上的空间,至少会认为它和物理内存都可以被矩形图描绘出来,看上去好像它能容纳什么一样。其实虚拟地址也就是处理器可能的寻址,虚拟地址空间就是处理器的寻址空间,是一个数学上的空间,也就是一个集合,是处理器寻址时能产生的所有的二进制数字的集合{0x00000000,0x00000001,…..,0xFFFFFFFF}。所以,还不如把他叫做处理器寻址集合,而所谓的4K大小的虚拟内存页,也只是一个个处理器的寻址区间。牢骚发完,还是把它叫做虚拟地址空间吧。
虚拟地址空间(处理器寻址集合)的大小和处理器寻址能力有关,而处理器寻址能力和通用寄存器大小有关,如果通用寄存器大小为32bit,处理器寻址范围就是0x00000000-0xFFFFFFFF,大小为4G。虚拟地址空间又被分为4k大小的虚拟内存页(处理器寻址区间),故在通用寄存器大小为32bit时需要支持2^20(1M)个虚拟内存页。
处理器所支持的RAM容量是由处理器的地址引脚数量决定的,处理器地址引脚数目为32时,处理器所支持的RAM最大容量为4G。物理内存被分为4k大小的页帧,由于在处理器地址引脚为32的情况下可支持的RAM最大值为4G,故最大可支持物理内存页帧的数目是2^20(1M)个。
注:通用寄存器大小和处理器地址引脚数目未必相等,如在开启PAE模式时,处理器可以支持64G大小内存(36个地址引脚),但是每次寻址只能寻址小于4G的范围(32bit通用寄存器)。
说到了物理内存和虚拟地址空间(处理器寻址集合),就不得不说2者之间的衔接:页表。页表的作用就是关联虚拟地址(处理器的寻址)和物理内存,而且是单向的关联,只能通过虚拟地址(处理器的寻址)找到其对应的物理内存。
现在我们将情况限制在处理器地址引脚为32个,通用寄存器大小为32bit的情况。即处理器可以支持4G大小的物理内存,可以寻址4G大小的虚拟地址空间。我将分两部分讨论:1.页表如何关联物理内存;2.虚拟地址如何在页表中寻址。
页表如何关联物理内存
我们前面假设的情况下用于转换处理器的虚拟寻址和物理内存的地址的页表分为两层结构。
第一层是page directory(页目录表):pagedirectory每个进程对应一个,大小占用一个物理内存页(4K),包含1k个page directory entry(页目录项),每个page directory entry大小为32bit用来寻址一个page table(页表)。所以每个进程一共有1K个page table。
第二层是page table(页表)。每个pagetable大小也是4K(一个物理内存页),每个page table包含了1K个page table entry(页表项),每个page table entry大小为32bit用来寻址一个page,而每个page对应一个物理内存页。
现在由我们来算一算,一个物理内存页的大小是4K,page table中的每个pagetable entry都索引一个物理内存页。每个page table中一共有1024个page table entry,这样每个page table能索引1024个物理内存页,也就是4M。而每个page table都对应着page directory中的一个page directory entry,而page directory中有1024个page directory entry,所以每个page directory可以映射4G的物理内存。当然这是连内核地址空间也一同算上后,一个进程可以通过页表映射4G的物理内存,去掉内核地址空间后为3G,当然这是可映射的最大物理内存,通常情况下即使一个进程正在被执行,它的页表所关联的物理内存也远小于这个值。
注意page directory和单个page table的大小都是4K,占用整整一个物理内存页,这是为方便寻址,只需要寻找page directory和page table所在物理内存页帧的基址就可以了,而每个内存页的基址的低12位都是0,所以寻址page directory和page table以及物理内存页时只需要用到20bit就可以了(低12位默认为0),这样就可以腾出12bit位用来标识page table entry和page table entry的各种状态。
现在还剩下两个问题:1.pagedirectory所在的物理内存页如何定位。2.page directory entry如何定位其关联的page table以及page tableentry如何定位其关联的page。
关于第一个问题,由于page directory被保存在一整个物理内存页中,所以当某个进程正在被执行时,需要处理器随时能访问到这块物理内存页。处理器的CR3寄存器专门负责保存这个物理内存页的编号(20bit就可以)。
第二个问题,page directoryentry和page table entry都是32bit的数据,分别存在于page directory和page table中,他们的结构相同,32bit中的12bit用来标示他们关联的页或页表的状态,剩下20bit用来保存其关联的物理内存页或页表所在的物理内存页的编号。
虚拟地址如何寻址页表
上边讲完了关联物理内存,现在来说说通过虚拟地址寻址页表,在上述通用寄存器为32bit的情况下,cpu每次的寻址都是一个32bit的数据,大小范围为0x00000000-0xFFFFFFFF。在上述情况下,这个32bit的数据被分为3段。
第一段:page directoryindex,虚拟地址的前10bit,索引范围为1K,这10bit的数据表示page directory entry在page directory中的编号。在知道page directory的地址的情况下,可以用这个编号找到对应的page directory entry,从而通过page directory entry找到对应的page table。
第二段:page table index,虚拟地址的第11bit-20bit,索引范围为1K,这10bit的数据表示pagetable entry在page table中的编号,在通过第一段找到了page table之后,可以根据这段来定位到page table entry,从而找到对应的物理内存页帧。
第三段:offset,虚拟地址的最后12bit,索引范围为4K,这12个bit的数据表示在物理内存页中的位置,物理内存也大小为4K所以需要12bit来索引。
总结一下:
1. 处理器找到某个虚拟地址映射的物理内存时,先从其cr3寄存器中找到page directory所在的物理内存页。
2. 然后通过虚拟地址的前10bit找到其对应的page directory entry。
3. 然后用page directory entry中的某20bit为编号找到其关联的page table所在的物理内存页。
4. 通过虚拟地址的11-20bit作为编号,从page table中找到对应的page table entry。
5. 用page table entry中的某20bit为编号找到其关联的page。
6. 通过虚拟地址的最后12bit为偏移量,找到要访问的物理内存再page中具体位置。
前面讨论的都是在假设处理器的通用寄存器大小和处理器的地址引脚数目都是32的情况下,但是在开启pae模式的清下,地址引脚的数目是36,可以管理64G的物理内存,但是通用寄存器的大小仍然是32,只能寻址4G的虚拟空间。这种情况下是处理器如何将虚拟地址转变为物理地址呢?
当然还是通过页表,页表还是包含pagedirectory和page table,但是page directory不是最顶层,在PAE模式下又多了一层,为什么会多了这一层呢,下面我们来慢慢的描述。
上文讲到在处理器地址引脚为32的情况下,处理器可以管理4G的物理内存,而物理内存又划分为4K大小的物理内存页,所以在地址引脚为32的情况下,一共可以管理2^20个物理内存页,所以需要20个bit位才能索引所有的物理内存页。但是当处理器的物理引脚为36的情况下,处理器可以管理64G内存,但是内存仍然是划分为4K大小的物理内存页,所以在地址引脚数目为36的情况下,一共可以管理2^24个物理内存页,所以需要24个bit位才能索引所有的物理内存页。
记住这一点,当处理器的地址引脚数目从32变成36时,可管理的物理内存从4G变为64G,可管理的物理内存页从2^20编程2^24,索引所需的bit位从20编程24位。
前面讲了,物理内存页仍然是4K大小,而前面的前面又讲了,每个page directory和每个page table都是占用一个物理内存页,也就是4K的大小,这个是不变的。而上一段又介绍了索引bit位为从20变成了24bit,而每个page directory entry和每个page table entry要固定12bit位用作其他标志,这样12+24bit就超过了32bit,所以在pae模式下,每个page directory entry和page table entry都是64bit。
每个page directory和page table都是4K大小,现在pagedirectory entry和page table entry变为了64bit,那么每个page directory就只能包含512个page table entry,同理每个page table只能包含512个page table entry。
前面讲了当page directory和page table分别包含1024个page directoryentry和page table entry是,在虚拟地址中分别用了10bit来索引他们在page directory和page table中的位置。但是再PAE模式下,它们的个数从1024变成了512,所以在虚拟地址中分别只需要9bit就能够索引。
说了半天终于说到问题了,由于虚拟地址中分别只需要9bit就能索引,物理内存页的大小仍然是4K,那么页内偏移量仍然只需要12bit,这样9+9+12=30bit,虚拟地址还剩余两个bit,怎么办,怎么办?而且,512*512*4K = 1G,即现在的情况虚拟地址只能关联1G的物理内存。所以在PAE模式下又引入了一层表(page directory pointer table,PDPT),PDPT表只有4元素,通过虚拟地址的前2bit来索引,每个元素都指向一个pagedirectory。而在PAE模式下,处理器的cr3寄存器保存的是PDPT的地址。
总结一下
由于两个东西不变,导致了开启PAE模式的时候,页表从原来的2层结构变成了3层结构:
首先不变的是物理内存的页帧大小,这个大小仍然是4K,由于这个大小不变,导致了开启PAE模式下需要管理2^24个物理内存页帧,进而导致了page directory entry和page table entry在定位具体的物理页帧时需要付出24个bit位(比原来多4个),进而导致了原来的page directory entry和page table entry原有的32的bit不能满足需求(因为还有12个bit做其他用),使得page directory entry和page table entry从原来的32bit扩展到了64个bit。
第二个保持不变的是page directory和page table仍然是占用整整一个物理内存页,为了方便寻址,这样page directory和page table的大小就是4K,而由于page directory entry和page table entry大小从32bit变成了64bit,致使每个page directory和page table包含的entry从原有的1024编程了512,所以虚拟地址上的索引bit从原有的10bit降到了9个bit,进而索引两层页表只需要30个bit,进而剩余的两个bit只能另外在扩展一个pdpt表了。
PAE模式下通过32虚拟地址找到对应的物理内存页:
1. 处理器的一个32bit寻址地址,在CR3中找到PDPT的地址,该表为32字节大小。
2. 根据虚拟地址的前2bit定位到相关的PDPT entry,并据此找到page directory所在的物理内存页。
3. 然后就和上面的一般模式的相同了。