好 接下来我们讲交换和覆盖
这两种做法呢实际上是
我们在前面已经讨论到过的
但是为了能更好地说明
虚拟存储的作用和它的基本原理
我们有必要把前面这两个呢
再细化一下进行介绍
覆盖技术是说我有一个进程
要在内存当中运行 但是这个内存较小
而我的程序比较大 那没法再给它运行
我怎么做这个事 那它的做法呢是说
我需要把程序依据它的逻辑结构
把它划分成若干个功能相对独立的模块
然后把那些相互之间
没有调用关系的模块呢分成一组
它们共享一块内存区域 这样的话
我就可以让通过这种共享呢
让整个进程占用的存储空间变小
从而在一个小内存的系统上能运行
那具体说起来呢 在这里面呢我们就是
把程序的模块呢划分成必要的部分和可选部分
必要部分通常是一些常用的功能
而可选部分是些不常用 或者说在一些交替用的
那这些常用的呢让它常驻内存
而这些可选的部分呢 我只在它要用的时候
我把它装入内存 那有了这种做法之后呢
那是说这种不存在调用关系的
我可以把它相互覆盖
把它放到同一块内存区域里头来
好 那这种做法我们具体看看它怎么做呢
这地方呢给出来的是一个例子
说我有一个程序 它呢分成了六个模块
每个模块的大小呢在这儿有了标定
基本上是20 30 40 50这几个尺寸
然后它总共的大小呢是加在一起
是190K 那现在呢假定说
我在一个一台计算机系统上运行它
它的物理内存不够190K
那这时候你肯定没办法在上面运行了
好 那这时候呢我希望通过
我应用程序的处理使得可以让它能运行
那我们怎么办呢 先把它分成
按照相互独立的这几块
按照相互之间调用关系我把它分组
A和这些都有关系 它自己一组
B呢它和C E F都没关系
那我把C跟它分成一组
好 这个D呢 它肯定是要单独一组
因为这是一绺 好 那它和E和F是没有调用关系
它们俩可以在一组 E和F呢也是一样的
所以它们仨一组
这时候我怎么给它分配存储空间呢
是每一组里头我按它最大的来分配
那这时候呢我们分配完了是这样一种
A给20K B和C呢一个50 一个30我给50
剩下这个E F D这三个呢分别是30 20 40 我给40
我分了这样几块之后
那这时候它运行的时候会是啥样子呢
刚开始运行的时候A B和D在内存里头
它们之间运行 好运行到一段时间之后
那开始C调用的时候调用E
那这时候我把B和D换出去
然后把C和E拿进来
这时候在执行中间这一段
假定说你是在做数据处理
好 那它是可以运行的
好 等处理完了之后
那最后我要把结果输出来这是用的F
这时候F交换过来
因为这个F呢我们这一绺的大小
是以它们三块最大来弄的
所以这F放进来呢肯定是没问题的
好 用这种做法之后
我总共占用的存储空间是多少
40 50 20加在一起110 那它是可以运行的
好 那这样以来的话如果说你的系统里
只有110K的物理内存
那这个大一点的程序就没有办法运行
通过这种改造之后 使用覆盖技术
我写出来的程序跟你的功能是一样的
但是这时候它可以在110K的内存上运行
好 通过这种办法呢我可以
减少一个应用程序内存占用量
从而可以在小的 小内存的系统上运行
那这时候问这种做法需要了解这个关系
我现在这个画法是最优的吗
那实际上在这儿我们可以给出另一种划分办法
那A是独立一个模块
但是第二步呢我把B原来是和C放在一起
这俩差挺远
好 那这时候呢我把C B E和F放在一起
这两块呢它尺寸比较接近
好 那这时候我给它50K
而把最后一个呢C和D它俩放在一起
它俩都是30 这时候搁30
把这尺寸接近的放在一起之后呢
相互之间没有调用关系放在一起
好 这时候它合在一起
这几个加在一起是100 这个会更小
那是不是它是最小的
如果说你要严格去来讨论这个问题
那是相当复杂的 所以在这儿呢
这个给程序员的开销是很大的
那我们说在实际的系统里头呢
在我们的DOS操作系统上
这是历史上用过的 里头有个Turbo Pascal
那这是它的集成开发环境
使用Pascal语言的
好 那在这个环境里呢
它提供了这种Overlay的覆盖技术
那有了这个支持之后
那实际上它这里面是有一堆库支持
你在这里头呢这个模块的换入换出
和模块之间的关系的这种指定
好 那这时候说它的开发难度是会增加的
因为我要确定人首先要用程序员
来对模块进行划分 划分完了之后
还要确定它们之间的覆盖关系
那这时候呢 我的编程难度是增加的
与此同时我的执行时间呢也会有所增加
那这时候呢需要 原来我们执行的时候
是你把190K一块读入内存
然后你就开始执行了 后面就没有开销了
好 而在这儿呢
我不但要在刚开始的时候我读入一部分
那后来呢
我还会把另一部分再读进来
那这种呢就会导致你的执行时间会增强
那这种做法呢我们觉得它会有问题
好 另一种做法交换技术
这种做法呢是增加正在运行的程序
或者说需要运行程序的空间
这和我们前面说那个问题不太一样
说 我原来呢是一个程序的内存空间就不够用了
现在这儿呢 实际上讨论的是说
我一个程序你肯定要够用的
然后我只是当前这个程序呢
由于多道的程序运行使得
另一个应用程序占用了内存空间
使得它的空间不够
它并不讨论一个程序在所有内存空间里
用的时候它仍然不够的情况
好 那在这儿说 我们怎么做呢 你把那些
如果说你多个进程 同时在内存里头
我把那些另一些进程就把它暂停
并且放到外存里去
这样的话我的空间不就够用了吗
当前正在运行的或者说你需要运行的进程
它的地址空间就增大了
那这时候呢需要注意一条
它换入换出的基本单位是整个进程
那这个单位呢导致了不像我们刚才的
覆盖我是程序内部的事
好 然后 这时候有两个基本操作 一个是换出
我把一个进程的整个地址空间保存到外存里去
跟它对应着呢是换入 我把某个进程
在外存当中某个进程读到内存里头来让它能运行
那这是以进程为单位的交换技术
这种做法呢我们在前面也有这样一个示意图说
我在这里头两个进程 一个进程在内存里头
一个进程在外面 那现在说我要想让它运行
它需要的空间大 我就把它换出来
然后把它换进去 然后这样的话
它的空间呢就能够运行了
如果说这空间足够的时候呢
一半它也能进行的 我就可以让它俩在内存里头
这样你 你这个交替运行的时候
它的速度就很快 但这时候呢空间不够
好 这是交换
我们使用交换技术可以把一些
暂停执行的进程放到外存里头去
但这时候呢也会有一些麻烦
就是说首先第一个我们遇到麻烦
是说我在什么时候来进行交换
那这时候呢通常情况下我在内存不够的时候
或者有不够可能性的时候
比如说我一个正在执行的进程
它的内存空间不够用了
这时候我必须把另外一些暂停执行的
并且在内存里的
把它整个进程地址空间兑换到外存当中
这时候我这个可以扩大
另一种情况是说我有一个进程要执行
现在内存里可用的空闲空间不够用
好 那这时候我就把暂停的另外一些进程呢
倒到外存里面去 这时候它可以来运行
那这是时机
再有一个是交换区域的大小
也就是说我倒到外存里头去
放在外存里头这些进程映象
它要占多大空间呢
需要把所有的暂停的用户进程
全部保存到外存当中
这时候呢它是需要占用一定的存储空间的
还有一个问题是说 我换入的时候
那你是否能放回到原处呢 如果说不放回到原处
我原来的函数调用或者说有跳转 这些你怎么办
那这个时候呢我们说它的做法是
需要采用动态的地址映射的办法
而这些前面关于交换和覆盖的这种技术准备
都为我们的虚拟存储打下基础
好 那我们对它做一个比较 对于覆盖来说
它是在程序内部模块之间的
跟程序外边没关系 好 这时候它是一个进程
在物理空间里运行它就不够了
好 那这时候说我们需要兑换的呢
覆盖进行交换的呢是这个
没有调用关系的模块之间
程序员必须知道这种逻辑覆盖关系
这是比较麻烦的
而对于交换来说呢它是以进程整个地址空间
为单位来进行交换的
这时候呢我们不需要这个逻辑关系
它只会发生在进程之间
好 这一部分呢实际上可以由操作系统来做的
那上边这一部分可不可以由操作系统来做呢
那这事有难度 原因在于这地方这种逻辑关系
你操作系统没有办法很准确掌握的
好 那这样的话是说 我有没有可能以使用
由操作系统来做同时呢
我又是不是以整个进程为单位
是一部分一部分的
进程地址空间的一部分内容
我把它导入到外存里头去
这个时候有没有可能呢
那这就是我们下面要说到的
虚拟存储要来做的事情