单、多道编程操作系统的内存管理
1.单道编程的内存管理
在单道编程环境下,整个内存里面只有两个程序:一个是用户程序,另一个是操作系统。由于只有一个用户程序,而操作系统所占用的内存空间是恒定的,我们可以将用户程序总是加载到同一个内存地址上。即用户程序永远从同一个地方开始执行。
在这种管理方式下,操作系统永远跳转到同一个地方来启动用户程序。这样,用户程序里面的地址都可以事先计算出来,即在程序运行前就计算出所有的物理地址。这种在运行前即将物理地址计算好的方式叫做静态地址翻译。
固定地址的内存管理单元非常简单,实际上并不需要任何内存管理单元。因为程序发出的地址已经是物理地址,在执行过程中无须进行任何地址翻译。而这种情况的直接结果就是程序运行速度快,因为越过了地址翻译这个步骤。
缺点:
1.整个程序要加载到内存空间中去,将导致比物理内存大的程序无法运行。
2.只运行一个程序造成资源浪费,如果一个程序很小,虽然所用内存空间小,但剩下的内存空间也无法使用。
3.程序可能无法在不同的操作系统下运行,因为不同操作系统占用的内存空间大小可能不一样,使得用户程序的起始地址可能不一样。这样在一个系统环境下编译出来的程序很可能无法在另一个系统环境下执行。
2.多道编程的内存管理
多道编程可以极大地改善CPU和内存的效率,改善用户响应时间,但代价是操作系统的复杂性。
因为多道编程的情况下,无法将程序总是加到固定的内存地址上,也就是无法使用静态地址翻译。这样我们就必须在程序加载完毕后才能计算物理地址,也就是在程序运行时进行地址翻译,进行从虚拟地址->物理地址的翻译,这种翻译称为动态地址翻译。
多道编程下的内存管理策略有两种:固定分区和非固定分区。
2.1 固定分区的多道编程内存管理策略
固定分区的管理就是将内存分为固定的几个区域,每个区域的大小固定。最下面的分区为操作系统占用,其他分区由用户程序使用。这些分区大小可以一样,也可以不一样。考虑到程序大小不一的实际情况,分区的大小通常也各不相同。当需要加载程序时,选择一个当前闲置且容量够大的分区进行加载。
共享队列的固定分区策略
这种模式下,当一个新的程序想要运行,必须排在一个共同的队列里等待。当有空闲分区时,才能进行加载。由于程序大小和分区大小不一定匹配,有可能形成一个小程序占用一个大分区的情况,从而造成内存里虽然有小分区闲置,但无法加载大程序的情况。
分开队列的固定分区策略
为解决上述问题,我们可以将小程序加载到小分区里,让不同的分区有不同的队列,程序就可以分配到大小更合适的内存空间。当然,这种方式也有缺点,就是如果还有空闲分区,但等待的程序不在该分区的等待队列上,就将造成有空间而不能运行程序的尴尬处境。
2.2非固定分区的多道编程内存管理策略
非固定分区的思想很简单:当一个程序需要占用内存空间时,就在该片空间里面分出一个大小刚刚满足程序所需的空间;再来一个程序,则在剩下的空间里面再这样分出一块来。在这种模式下,一个程序可以加载到任何地方。
非固定分区内存地址管理的机制也很简单,使用基址+极限来进行地址翻译。
即:
物理地址=虚拟地址+程序所在区域的起始地址
这种方式可以对每一个程序配备两个寄存器:基址寄存器和极限寄存器。所有访问地址都必须在这两个寄存器值框定的空间里,否则就算非法访问。
非固定分区这种管理方式存在一个重大问题:如果程序在执行过程中需要更多空间,怎么办?解决的办法当然是在一开始给程序分配空间时就分配足够大的空间,留有一片闲置空间供程序增长用。不过,在分配增长空间后需要考虑一个问题。一个程序的空间增长通常有两个来源:数据和栈。
分配增长空间
1.数据和栈往一个方向增长。缺点:当下面的数据长到上面数据所分配空间的底时,就无法再长了;而移动栈底非常不方便。
2.数据和栈相对而生长。这是UNIX采取的策略。
重叠
如果一个程序超过了物理内存,还能运行吗?
其实是能的。这个办法就是重叠。
重叠,就是将程序按照功能分成一段一段功能相对完整的单元,一个单元执行完后,再执行下一个单元,而一旦执行到下一个单元,就不会再执行前面的单元。所以我们可以把后面的程序单元内存数据覆盖到当前程序单元上。这样就可以执行一个比物理内存还要大的程序。
但是这相当于把内存管理的部分功能交给了用户,不能算是操作系统提供的解决方案。
双基址
如果我们运行两个一样的程序,只是数据不同,我们自然想到能否让两个程序共享部分内存空间。例如,如果我们同时启动两个PPT演示文稿,我们希望PPT的程序代码部分能够共享。
设定两组基址和极限。数据和代码分别用一组基址和极限表示,这就解决了问题。
2.3 交换内存管理
问题是程序在运行之前,我们无法知道它需要内存的准确大小,所以内存增长的问题仍需解决,而交换就是一个方式。
交换是当一个程序所占空间不够时,我们将其倒到磁盘上,再加载到一片更大的内存空间。
总结
到目前为止,本书已经介绍了几种基本的内存管理方法,分别是固定加载地址的内存管理、固定分区的内存管理、非固定分区的内存管理和交换内存管理。其中,以交换内存管理最为灵活和先进。它可以解决因程序所需空间增长而无法继续运行的困难,又可以实现动态多道编程。
但事实上,这种策略存在很多重大问题,而其中最重要的两个问题是空间浪费和程序大小受限。
为解决这些问题,出现了页式内存管理和段式内存管理。
参考资料:
《操作系统之哲学原理》 邹恒明著