1、虚拟内存的基本思想:
每个程序拥有自己的地址空间,这个地址空间被分割成多个块,每一块称作一页或页面。每一页有连续的地址范围。这些页被映射到物理内存,但并不是所有的页都在内存中才能运行程序。当程序引用一部分在物理内存中的地址空间时,有硬件立刻执行必要的映射。当程序引用到一部分不在物理内存中的地址空间时,由操作系统负责将缺的部分装入物理内存并重新的执行失败的指令。
虚拟内存很适合在多道程序设计系统中使用,许多程序的片段同时保存在内存中。当一个程序等待它的一部分读入内存时,可以把CPU交给另一个进程使用;
2、分页
大部分虚拟内存中使用一种分页的技术。
由程序产生的这些地址称为虚拟地址,他们构成了一个虚拟地址空间,在使用虚拟内存的情况下,虚拟地址不是被直接送到内存的总线上,而是被送到内存管理单元(MMU),MMU把虚拟地址映射为物理内存地址;如图:
图3-9中例子说明了映射是如何工作的。在例子中有一台可以产生16位地址的计算机,地址的范围从0-64k,且地址是虚拟地址。然而这台计算机只有32K的物理内存,So 编写64K的程序,但他们不能完全的调入内存运行。在磁盘上必须有一个可以大到64K的程序核心映像的完全副本,以保证程序片段在需要时被调入内存。
虚拟地址空间按照固定大小划分称为页面的若干单元。在物理内存中对应的单元称为页框。页面和页框的大小通常是一样的。
图3-9的标记符号如下:标记0-4K的范围表示该页面的虚拟地址或物理地址0-4095,4-8K的范围表示地址4096-8191,等等。每一页包含了4096个地址,起始4096的整数倍位置,结束于4096倍数缺1;
3.实现原理:
当程序试图访问地址0时,例如执行如下指令:
MOV REG 0
将虚拟地址0送到MMU。MMU看到虚拟地址落在页面0(0-4095),根据其映像结果,这一页面对应的是页框2(8192-12287),因此MMU吧地址变换为8192,并把地址8192送到总线上。内存MMU一无所知,它只看到一个读或写地址8192的请求并执行它。MMU从而有效地吧所有从0-4095的虚拟地址映射带到了8192-12287的物理地址。
4.查看MMU的内部结构及了解MMU是如何工作的:
在下图会看到一个虚拟地址的例子,虚拟地址8196(二进制是0010000000000100)用图3-9所示的MMU映射机制,输入的16位虚拟地址被分为4位的页面号和12位的偏移量,4位页号可以表示16个页面,12位的偏移可以为一个页内的全部4096个字节的编址。
可用页号作为表页的索引,以得出对应的该虚拟页面的页框号。如果“在/不在”位是0,则将引起一个操作系统陷阱。如果该位是1,则将在页表中查到的页框号复制到输出寄存器的高3位中,在加上输入虚拟地址中的低12位偏移量。如此就构成了15位的物理地址。输出寄存器的内容即被作为物理地址送到内存总线。
5.页表项的结构:
虚拟页号可用作页表的索引,已找到该虚拟页面对应的页表项。由页表项可以找到页框号。然后把页框号拼接到偏移量的高位端,以替换掉虚拟页号,形成送往内存的15位物理地址。
页表的目的是把虚拟页面映射为页框。从数学的角度看,其好比一个函数,输入为虚拟页号,输出为物理页框号。
不同的计算机页表项的机构会不同;
最重要的域是页框号,毕竟页映射的目的是找到这个值。
其次是“在/不在”位,这位是1时表示该项是有效地,可以使用;如果是0,则表示该项对应的虚拟页面现在不在内存中,该页面会引起缺页中断(到内存中找指定页面没有,就会产生缺页中断)。
”保护“位指出一个页允许什么类型的访问。
为了记录页面的使用状况,引入”修改位“ 和”访问位“ 在写入一页是由硬件自动的设置修改位。修改位在操作系统重新分配页框时非常有用。如果一个页面已经被修改过,则必须要把它写回磁盘。如果一个页面没有被修改过,则只简单的把它丢弃就可以了,因为它在磁盘上的副本仍然是有效地。
高速缓存禁止位:对那些映射到设备寄存器而不是常规内存的页面而言,这个特殊性是非常重要的;
6.加速分页过程:
在任何分页系统中,都需要考虑两个问题:
(1) 虚拟地址到物理地址的映射必须非常快;
(2) 如果虚拟地址空间很大,页表也会很大。
解决的方案:
针对第一个问题:可以用”转换检测缓冲区TLB“:
TLB这种方案的建立是基于这样一种现象:大多数程序总是对少量的页面进行多次的访问,而不是相反的。因此,只有很少的页表会被反复读取,而且他的页表项很少被访问;通过这个方案为计算机设置了一个小型的硬件设备,将虚拟地址直接映射到物理地址,而不是在访问页表。这种设备称为”转换检测缓冲区“.
TLB如何工作:
将一个虚拟地址放入MMU中进行转换时,硬件首先通过将该虚拟页号与TLB中所有表项同时(即并行)进行匹配,判断虚拟页面是否在其中。如果发现了一个有效的匹配并且要进行的访问操纵并不违反保护位,则将页框号直接从TLB中取出而不必再访问页表。如果虚拟页面号确实是在TLB中,但指令试图在一个只读页面上进行写操作,则会产生一个保护错误,就像对页表项的非法访问一样。
如果MMU检测到没有有效地匹配项时,就会进行正常的页表查询。接着从TLB中淘汰一个表项,然后用新找到的页表项代替它。这样,如果这一页面很快再被访问,第二次访问TLB时自然将会命中而不是不命中。当一个表项被清除出TLB时,将修改位复制到内存中的页表项,而除了访问位,其他的值不变。当页表项中从页表装入到TLB中时,所有的值都会来自内存。
针对大内存的页表,有两种解决方法:
1、多级页表:
一个简单的例子如图3-13a,32位的虚拟地址被划分为10位的PT1域,10位PT2域和2位的Offset(偏移量)域。因为偏移量是12位,所以页面长度是4KB,共有2^20个页面。
引入多级页表的原因是避免把全部页表一直保存在内存中。特别是那些从不需要的饿页表就不应该保留。
考察图3-13b例子中的二级页表是如何工作的。在左边是顶级页表,它具有1024个表项,对应于10位的pT1 域。当一个虚拟地址被送到MMU时,MMU首先提取pT1域并把该值作为访问顶级页表的索引。因为整个4GB虚拟地址空间已经被分为1024个4MB的块,所以这1024个表项中的每一个都表示4MB的虚拟地址空间。
由顶级页表得到的表项中含有二级页表的地址或页表框号。顶级页表的表项0指向程序正文的页表,表项1指向数据的页表,表项1023指向的堆栈的页表,其他的表项未用。现在把PT2域作为访问选定的二级页表的索引,以便找到该虚拟页表的对应页框号。
举个例子:
考虑虚拟地址0x00403004(十进制4206596)位于数据部分12292字节处。它的虚拟地址对应PT1=1, PT2=2, Offset=4.MMU首先用PT1作为索引访问顶级的=页表得到表项1,它对应的地址范围是4-8M,然后,它用PT2作为索引访问刚刚找到的二级页表并得到表项3,它对应的虚拟地址范围是在它的4M块内的12288-16383(即绝对地址4106592-4210687)。这个表项含有虚拟地址0x00403004所在页面的页框号。如果页面不在内存中,页表中的”在/不在“位将是0,引发一次缺页中断。如果该页面在内存中,从二级页表中得到的也框号将与偏移量结合形成物理地址。
2. 倒排页表
对于32位虚拟地址空间,多级页表可以发挥的很好,但对于64位的计算机情况发生了彻底的变化,因而64位分页的虚拟地址空间的系统需要一个不同的解决方案。
倒排页表的方法可以节省大量的空间,但也存在不足,主要在机器的运行效率上。
走出这种两难的局面的办法是使用TLB。如果TLB能够记录所有频繁使用的页面,地址转换就可能变得像通常的页表一样快。但是,当发生TLB失效时,需要用软件搜索整个倒排页表。一个可行的实现该搜索的方法是建立一张散列表,用虚拟地址来散列一当前所有在内存中的具有相同散列值的虚拟页表被链接在一起,如图3-14,如果散列表中的槽数与机器中物理页面数一样多,那么散列表的冲突链的平均长度将会是1个表项,这将会大大提高映射的速度。一旦也框号被找到,新的(虚拟页号、物理叶框号)对就会被装载到TLB中。
参考:
《现代操作系统》 --- 虚拟内存
学习笔记-操作系统虚拟内存篇
操作系统管理内存的机制——为什么要设置虚拟内存?
Linux 虚拟内存和物理内存的理解