虚存的起源
(从二维方块到3D和动画CG)电子游戏所占的内存越来越大,对计算机要求的性能也就越来越过。也就说程序规模的增长远大于储存器容量增长的速度。
我们理想中的存储器:更大更快且非易失性存储器。实际的物理硬件是很难获取的。
如图所示,快速访问的存储器都靠近CPU但是容量低且造价高。从下往上,意味着价格越来越高,容量越来越小,速度越来越快。硬盘存储空间 够大,但是速度不够,所以我们只能把程序放到内存上去执行;那么有没有什么办法能够把硬盘空间用于执行程序?===>虚存【把不常用的数据放入硬盘,把常用的数据放入内存,使得有限的内存放经常访问的数据,形成一种虚拟的大内存的感觉】
这种方式需要硬件MMU(内存管理单元)和OS内核一起完成。
覆盖技术:常用的代码和数据放入内存,不常用的数据和代码放入硬盘
.交换:内存中会跑多个程序,把一些当前优先级不高的程序放到硬盘中,使当前执行的程序会有一个更大的内存去执行。交换技术可由OS完成,但是开销比较大,因为需要导出整个程序到硬盘和导入内存,程序越大,导入导出的代价越大。
虚存技术:只把程序的一部分数据导入导出硬盘,减少交换技术的开销。以页和段为粒度,来把数据和代码导入导出,从而有效使用有限的物理空间。
覆盖技术
目标:在较小的可用内存中运行较大的程序
依据程序逻辑结构,将程序划分为若干功能相对独立的模块;将不会同时执行的模块共享同一块内存区域。【在不同区间,这些程序块可以不同时运行】某一段时间某一个区间执行,另一个时间另一个区间执行。但是这些函数分时共享一个内存空间。
必要部分:一定要有的代码和数据常驻内存,他来负责决定某个时间段把相应区间的函数导入或者导出内存
可选部分(不常用功能):其他程序执行中放入外存中或放在其他程序模块中,只在需要用到时装入内存
不存在调用关系的模块可相互覆盖,共用同一块内存区域
如图所示,他们之间的逻辑关系是:A调用BC,B调用D;C调用E或者F
A是常驻内存,因为他要调用其他所有函数。B和C都被A调用,且BC不会相互调用,这说明他们是隔离且独立的,所有可以把BC分在一个区,同理DEF分为另一个区。
内存总110K,程序总容量190K,也就说想把这个程序全部装入内存,空间是不够的。此时根据覆盖技术:A作为常驻内存放入常驻区。然后调用B和C中的一个,必然存在先后顺序,且其中一个执行时,另一个一定不执行,则只在需要时进行导入和导出操作即可【代码是只读的,他只需要释放空间不需要其他开销】
同理根据相互独立原则,还可以找出多种不同的覆盖方式。比如B和EF相互独立,C和D相互独立的。即他们之间不会相互调用,所以他们可以共享一个覆盖区。则此时覆盖区0为50K空间(装BEF),覆盖区1为30K空间(装CD),则比着如图所示的分区方法,总内存使用空间量还要少10K。
缺点:开销大。
1.设计的开销,程序猿需要考虑怎么把一个大的程序根据逻辑关系划分出一个个小的相互之间可以覆盖的功能模块,增加了程序猿的负担
2.不同执行阶段的对内存的导入和导出其实就是对硬盘的读写操作,这个过程时间开销也很大。
交换技术
导入导出的开销相对来说是比较大的,一般最小粒度也大于了一个页。逻辑如下:
交换存在的问题:
1.交换时机:如果程序执行过程中频繁交换,其实对整个系统的负荷很大。每一次的硬盘操作会有较长的时间开销,会耽误程序的快速执行,所以只有当且仅当内存空间不够用时,才会进行交换操作,而且要尽量减少这种操作。
2.所需空间[交换区的大小]:必须足够大以存放所有用户进程的所有内存映像的拷贝,必须对这些内存映像直接存取。假设一个内存中可以跑10程序,那么极端的情况下可能会把9个程序导出去,所以所需空间的预设计就要求比较大。
3.程序的重定位:程序执行时换出再换入后,内存位置可能发生了变化,这时候当前空闲空间和换出位置不同,那么寻址就可能出现问题。一般来说,采取动态映射的方法可以比较好的处理这个问题。【类似上一章讲解的虚拟地址和物理地址之间的映射表】
交换技术由操作系统来完成,对程序猿是透明的,程序猿不需要了解什么时候换入和换出,由OS自主管理。
交换和覆盖的比较
简单概括:
覆盖:小范围一个程序内
交换:大范围多个程序间