一:概述
如果程序太大了,超过了空闲内存的容量,那么就没有办法把它全部载入内存,这时该肿么办?
其实在很多年前,人们碰到这个问题时,通常采用的解决方案就是覆盖技术。
也就是说,把该程序划分为若干个部分,称为覆盖块overlay,然后只需要把当前用到的指令和数据保存
在内存中,而把其余的指令保存在外存中。
对于放在磁盘上的覆盖块,当需要用到他们时由操作系统自动载入他们到内存
虽然覆盖块的交换操作是由操作系统完成的, 但是必须由程序员手工的把一个大程序划分为若干个小的
功能模块,并确定各个模块之间的条用关系。 这种事情既费时又费力,增加了编程的复杂度。
于是人们想啊想,不久找到了一种解决方法,可以把全部的工作都交给计算机完成。
这种方法就是虚拟存储器
它的基本思路是:程序的代码,数据和栈的总大小可以超过实际可用的物理内存的大小。操作系统把当前
需要用到的部分保留在内存中,而把其余的部分保留在磁盘上。
例如:一个大小为 512MB 的程序,可以运行在一台内存空间为256MB 的机器上。采用的方法就是在进程运行的每时每刻,经过细心挑选,
只保留一半的程序代码和数据在内存中,然后需要的时候,再把各个程序片段
在内存和磁盘之间来回交换。
虚拟存储页管理:
大部分虚拟存储系统采用的是一种称为分页paging的技术。
由程序产生的地址称为虚拟地址virtual address,他们构成了一个虚拟地址空间virtual address space。
如果计算机使用了虚拟存储机制,虚拟地址不是直接放在地址总线上,而是被发送到存储管理单元MMU( Memory Managerment Unit) 中,
由它负责把虚拟地址映射为物理地址。如下图:
在虚拟页式存储管理中,一方面,会把物理内存划分为许多固定大小的内存块,称为物理页面,或者页框(page frame)。
另一方面,把虚拟地址也划分为大小相同的块,称为虚拟页面,或者简称为页面(page)。页面的大小要求是2的整数次幂,一般在512个字节到1M字节之间。
在本例中,页面的大小是4kb。 由于虚拟地址是 64KB,物理地址是 32KB,因此我们得到16个虚拟页面和8个物理页面。
当一个用户程序需要在内存和磁盘之间换入换出时,不是以整个程序为单位,而是以页面为单位。
假设程序运行时,需要去访问地址0,例如,如下的指令:(见下图)
MOV REG, 0
虚拟地址0发送给MMU,然后MMU就会发现整个虚拟地址位于第0个页面中(第0个页面所包含的地址0 - 4095),而第0个虚拟页面存放在内存的
第2个物理页面中(8192 - 12287),它的起始地址是 8192,然后再加上页内的偏移地址 0,所以最后得到的物理地址是 8192。
然后MMU就把这个真正的物理地址发送到地址总线上,从而去访问相应的内存单元。
这样,这一次的内存访问就完成了。
类似的如果去执行另外一条指令:(见下图)
MOV REG, 8192
那么该指令会被转为:
MOV REG, 24576
这是因为虚拟地址8192位于第二个虚拟页中,而该页面存放在第6个物理页面中(相应的物理地址为 24576 - 28671)。
第三个例子:(见下图)
虚拟地址为20500位于第5个虚拟页面中(虚拟地址为20380 - 24575),页面内的偏移地址是20. 而第5个虚拟页面存放着第3个物理页面,第3个物理页面的起始地址为 12288,
所以随后的物理地址为 12288 + 20 = 12308
经过适当的设置后,MMU能够将16个虚拟地址页面映射到相应的8个物理页面之一,但这并没有解决虚拟空间比物理空间大的问题。因为只有8个物理页面,因此在上图中,
只有8个虚拟页面能够被映射到物理内存。而其他的虚拟内存,无法映射,在图中用X来表示,
但是在实际的硬件上,会有一个 有效位(present/absent bit)来描述虚拟页面是否在内存中。
如果程序访问了一个未被映射的页面,比如,执行了下面的命令:
MOV REG, 32780
也就是对虚拟地址 32768 进行访问(这个虚拟地址位于第8个虚拟页面中,而且页面内便宜地址为12),那么MMU在进行地址映射时候就会发现,
第8个虚拟页面并未在内存中(在图中用X标识),因此无法完成映射,这时候,MMU会引发一个缺页中断(page fault),把这个问题提交给操作系统去处理。
操作系统会从内存中挑选一个使用不多的物理页面,把它的内容写回到磁盘,从而腾出一个空闲页面,然后把引发缺页中断的那个虚拟页面装入该空闲页面中,
并对地址映射关系进行更新。 最后,回到被中断的指令重新开始执行。
《操作系统设计与实现》 笔记