以前的一片文章讲了为什么会出现分页, 是为了解决什么问题?
- 首先澄清一个概念.其实虚拟内存和分页是两件事, 分页是指将把地址空间人为地等分成固定大小的页, 这个页的粒度比段更小, 以方便在外存与内存换入换出, 而虚拟内存是为了解决程序的加载和重定位的问题, 即解除线性地址与物理地址之间的相等关系, 因为如果这样, 线性地址是由编译器翻译出来的, 它本身是连续的, 而物理地址也必须连续才行, 但物理内存往往在运行时间长了之后, 成为各个碎片, 这就导致物理地址很难成为连续的(所以平时使用完电脑要记得关机), 所以要建立一种线性地址与物理地址之间的映射关系.而且虚拟内存可以同样解决程序重定位问题, 所以这就是天选之子啊! 对, 忘了说, 虚拟内存就是说逻辑上建立一张表, 让每个进程都拥有一张固定大小的地址空间,是假的, 只是逻辑上建立一张映射关系. 现代操作系统都是让每个进程逻辑上拥有4GB的空间.
- 由于分页功能的出现, 弱化了人们关于分段机制是否合理的信心. 内存的访问是通过页目录表和页表进行的, 每个任务都有自己的页目录表和页表, 操作系统控制着物理页的分配权, 除非他把一个也分配给某个任务, 并填写到那任务的页目录表和页表里, 否则, 那个任务不可能拥有访问那个内存位置的权利. 但分页机制还是建立在分段机制之上的. Intel之所以这么多年长盛不衰的原因, 我想很大一部分是由于他的向下兼容性, 所以即使这个机制已经没有作用了, 但是还是的用, 所以在保护模式下, 还是要经过段部件处理后, 得到线性地址, 然后经过页部件转换, 得到物理地址. 尽管处理器的设计者一直在宣称, 把分段和分页机制结合在一起, 将获得最大强度的保护功能, 但是, 事实上, 在现实的软件设计者那里, 多段模式已经不那么吃香了.
典型的, 32位的处理器拥有32根地址线, 64位处理器拥有64根地址线, 这使得他不用将4GB或多于4GB的内存空间划分成多个段, 就能完全控制它. 如此一来, 软件设计者就会倾向于不分段 . - 平坦模式(Flat Model): 将全部的4GB内存整体上作为一个大段来处理, 而不是分成小的区块. 在这种模型上, 所有的段都是4GB, 每个段的段描述符都指向4GB的段, 段的基地址都是0x00000000, 段界都是0xFFFFF, 粒度为4KB.
启用分页机制,我们需要做三件事:
- 准备好页目录表及页表
- 将页表地址写入控制寄存器cr3
- 寄存器cr0的PG位置1
首先, 开始说第一件, 建立页目录表和页表
分页机制本质上将大小不同的大内存段拆分成大小相等的小内存块. 内存块数 * 内存块大小 = 4GB, 现在普遍采用的虚拟地址空间为4GB.
滑块的位置不能太偏左, 内存块太大, 会导致程序的换入换出不方便, 物理内存的利用率低, 滑块的位置不能太偏右, 内存块太小, 会导致页表占用内存过多.而现在操作系统普遍将内存块的尺寸规定为12位, 4KB.
每个进程逻辑上有4GB的地址空间, 4GB/ 4KB = 1M, 所以需要1M个页表项, 32位下物理内存也为4GB, 所以索引到一个具体的物理地址上, 也需要32位, 如果进程使用一级页表的话, 需要1M * 4B = 4MB, 每个进程需要4MB的物理内存存储页表. 但是对于大多数程序, 他使用不了所有的内存空间, 所以页表项大多都是空的, 这样就引入了二级页表
二级页表:
由上面我们能得出结论, 我们需要1M个页表项, 1M = 1K * 1K, 而一张页表大小= 4KB, 4KB / 4B = 1K, 所以我们可以一张页目录表, 1K张页表, 而如果有连续的空间空闲, 比如 n * 1k * 4KB, 那是不是这n张页表就可以不分配内存, 而有一张页目录表就可以标志n张页表在物理内存中不存在, 这样就能节省下来物理内存.
接下来开始说, 二级页表地址转换原理.
由于页的大小都是4KB, 所以页表项中的物理地址都是4K的整数倍, 所以可以讲32位的地址划分成20:12, 前20位用来索引物理页, 后12位用来确定偏移地址.
在二级页表是这样的:
每个页表中可容纳 1024 个物理页,故每个页表可表示的内存容量是 1024 * 4KB = 4MB。页目录中共有1024 个页表,故所有页表可表示的内存容量是 1024 * 4MB =4GB ,这己经达到了 32 位地址空间的最大容量。
所以说,任意一个 32 位物理地址,它必然在某个页表之内的某个物理页中。我们定位某 个物理页,必然要先找到其所属的页表。页目录中 1024 个页表,只需要 10 位二进制就能够表示了,所以,虚拟地址的高10 位(第 31 ~22 位)用来在页目录中定位一个页表,也就是这高 10 位用于定位页目录中的页目录项 PDE,PDE 中有页表物理页地址。找到页表后,到底是页表中哪一个物理页呢?由于页表中可容纳 1024 个物理页,故只需要 10 位二进制就能够表示了。所以虚拟地址的中间 10 位(第 21 12 位)用来在页表中定位具体的物理页,也就是在页表中定位一个页表项PTE, PTE中有分配的物理页地址。由于标准页都是 4KB, 12位二进制便可以表达 4KB之内的任意地址,故线性地址中余下的 12 (第 11 位)用于页内偏移量。
经以上分析,二级页表地址转换原理是将 32 位虚拟地址拆分成高 10 位、中间 10 位、低 12 位三部分,它们的作用是:高 10 位作为页表的索引,用于在页目录表中定位一个页目录项 PDE,页目录项中有页表物理地址,也就是定位到了某个页表。中间 10 位作为物理页的索引,用于在页表内定位到某个页表项 PTE ,页表项中有分配的物理页地址,也就是定位到了某个物理页。低 12 位作为页内偏移量用于在已经定位到的物理页内寻址。同一级页表一样, 访问任何页表内的数据都要通过物理地址。由于页目录项 PDE 和页表项 PTE 都是4字节大小,给出了 PDE和PTE索引后,还需要在背后悄悄乘以4 ,再加上页表物理地址,这才是最终要访问的绝对物理地址。转换过程背后的具体步骤如下。
到了现在, 我们该讲讲页目录项和页表项的事啦, 一直说它是4字节大小, 用来存储物理页地址, 但也一直未曾见过他们的真面目.
具体位什么意思, 此处不多加阐述.
其他有待补充…