1、基址加界限机制(动态重定位)
1.1 该技术的出现时间在20世纪50年代后期。具体来说,每个CPU需要两个硬件寄存器:基址寄存器 和 界限寄存器。采用这种方式,在编写和编译程序时假设地址空间是从零开始。进程中使用的内存引用都是虚拟地址,硬件接下来将虚拟地址加上基址寄存器中的内容,得到物理地址,再发给内存系统。如果进程需要访问超过界限寄存器中设定的最大虚拟地址,或者访问为负数的虚拟地址,CPU将触发异常,进程最终可能被终止。
1.2 简单的通过基址寄存器和界限寄存器实现的虚拟内存很浪费,因为栈和堆之间的很多空间可能并没有被进程使用,却依然占用了实际的物理内存。此外,如果剩余物理内存无法提供连续区域来放置完整的地址空间,进程便无法运行。
2、分段机制
2.1 该技术的出现时间在20世纪60年代初期。这个想法很简单,在MMU中引入不止一个基址和界限寄存器对,而是给地址空间内的每个逻辑段一对。一个段只是地址空间里的一个连续定长的区域,在典型的地址空间里有3个逻辑不同的段:代码、栈和堆。
2.2 硬件在地址转换时使用段寄存器。它如何知道段内的偏移量,以及地址引用了哪个段?
- 显示方式:用虚拟地址的开头几位来标识不同的段。
- 隐式方式:通过地址产生的方式来确定。比如:如果地址由程序计数器产生,则地址在代码段。如果基于栈或基址指针,则地址在栈段。其他地址则在堆段。
2.3 支持许多段需要进一步的硬件支持,并在内存中保存某种段表。
2.4 在分段机制中,由于段的大小不同,空闲内存被割裂成各种奇怪的大小,因此满足内存分配请求可能会很难。另外,对于很大但很稀疏的段,仍然必须完整地加载到内存中,此时,分段机制就不能很好地工作。
3、分页机制
3.1 将一个进程的地址空间分割成固定大小的单元,每个单元称为一页。虚拟地址由 虚拟页号 和 页内偏移量 两部分组成,在地址转换时,首先访问页表,得到虚拟页号对应的物理页号,然后用 物理页号 替换 虚拟页号 来得到物理地址,此时偏移量保持不变(即未翻译)。
3.2 分页机制需要先执行一次额外的访存操作,以便首先从页表中得到物理页号。这个额外的内存访问开销很大,解决这个问题,需要TLB的帮助。对每次内存访问,硬件先检查TLB,看看其中是否有期望的转换映射,如果有,就完成转换,不用访问页表。
3.3 分页机制还引入了第二个问题:页表太大,因此消耗的内存太多。有很多方法可以解决,基本思路都是减小页表占用的物理内存,主要包括:
- 使用更大的页,以便减少页表项,进而减小页表。
- 分页和分段同时使用,为每个逻辑分段单独提供一个页表。
- 采用多级页表。整个页表在物理内存中存储时,占用了多个物理页(比如n个)。但其中一些物理页(比如m个,m < n)里面的页表项全是无效的,那么这些物理页里面的数据就可以被清除,留作他用。
参考资料:
1、《操作系统导论》,[美] 雷姆兹·H.阿帕希杜塞尔( Remzi H. Arpaci-Dusseau) [美]安德莉亚·C.阿帕希杜塞尔(Andrea C. Arpaci-Dusseau)。