0 00:00:00,000 --> 00:00:07,240 1 00:00:07,280 --> 00:00:12,200 好 接下来我们讲交换和覆盖 2 00:00:12,240 --> 00:00:14,240 这两种做法呢实际上是 3 00:00:14,280 --> 00:00:16,800 我们在前面已经讨论到过的 4 00:00:16,840 --> 00:00:18,960 但是为了能更好地说明 5 00:00:19,000 --> 00:00:22,440 虚拟存储的作用和它的基本原理 6 00:00:22,480 --> 00:00:24,440 我们有必要把前面这两个呢 7 00:00:24,480 --> 00:00:27,280 再细化一下进行介绍 8 00:00:27,320 --> 00:00:31,160 覆盖技术是说我有一个进程 9 00:00:31,200 --> 00:00:34,360 要在内存当中运行 但是这个内存较小 10 00:00:34,400 --> 00:00:37,680 而我的程序比较大 那没法再给它运行 11 00:00:37,720 --> 00:00:42,640 我怎么做这个事 那它的做法呢是说 12 00:00:42,680 --> 00:00:45,760 我需要把程序依据它的逻辑结构 13 00:00:45,800 --> 00:00:50,440 把它划分成若干个功能相对独立的模块 14 00:00:50,480 --> 00:00:52,240 然后把那些相互之间 15 00:00:52,280 --> 00:00:55,360 没有调用关系的模块呢分成一组 16 00:00:55,400 --> 00:00:59,440 它们共享一块内存区域 这样的话 17 00:00:59,480 --> 00:01:01,600 我就可以让通过这种共享呢 18 00:01:01,640 --> 00:01:04,360 让整个进程占用的存储空间变小 19 00:01:04,400 --> 00:01:07,640 从而在一个小内存的系统上能运行 20 00:01:07,680 --> 00:01:10,440 那具体说起来呢 在这里面呢我们就是 21 00:01:10,480 --> 00:01:18,160 把程序的模块呢划分成必要的部分和可选部分 22 00:01:18,200 --> 00:01:21,520 必要部分通常是一些常用的功能 23 00:01:21,560 --> 00:01:26,480 而可选部分是些不常用 或者说在一些交替用的 24 00:01:26,520 --> 00:01:29,640 那这些常用的呢让它常驻内存 25 00:01:29,680 --> 00:01:33,840 而这些可选的部分呢 我只在它要用的时候 26 00:01:33,880 --> 00:01:36,800 我把它装入内存 那有了这种做法之后呢 27 00:01:36,840 --> 00:01:40,240 那是说这种不存在调用关系的 28 00:01:40,280 --> 00:01:42,040 我可以把它相互覆盖 29 00:01:42,080 --> 00:01:45,200 把它放到同一块内存区域里头来 30 00:01:45,240 --> 00:01:49,480 好 那这种做法我们具体看看它怎么做呢 31 00:01:49,520 --> 00:01:51,480 这地方呢给出来的是一个例子 32 00:01:51,520 --> 00:01:55,760 说我有一个程序 它呢分成了六个模块 33 00:01:55,800 --> 00:01:58,560 每个模块的大小呢在这儿有了标定 34 00:01:58,600 --> 00:02:02,960 基本上是20 30 40 50这几个尺寸 35 00:02:03,000 --> 00:02:05,800 然后它总共的大小呢是加在一起 36 00:02:05,840 --> 00:02:09,440 是190K 那现在呢假定说 37 00:02:09,480 --> 00:02:12,200 我在一个一台计算机系统上运行它 38 00:02:12,240 --> 00:02:14,800 它的物理内存不够190K 39 00:02:14,840 --> 00:02:17,120 那这时候你肯定没办法在上面运行了 40 00:02:17,160 --> 00:02:19,040 好 那这时候呢我希望通过 41 00:02:19,080 --> 00:02:23,440 我应用程序的处理使得可以让它能运行 42 00:02:23,480 --> 00:02:26,360 那我们怎么办呢 先把它分成 43 00:02:26,400 --> 00:02:28,360 按照相互独立的这几块 44 00:02:28,400 --> 00:02:31,040 按照相互之间调用关系我把它分组 45 00:02:31,080 --> 00:02:33,840 A和这些都有关系 它自己一组 46 00:02:33,880 --> 00:02:38,240 B呢它和C E F都没关系 47 00:02:38,280 --> 00:02:40,960 那我把C跟它分成一组 48 00:02:41,000 --> 00:02:44,480 好 这个D呢 它肯定是要单独一组 49 00:02:44,520 --> 00:02:49,440 因为这是一绺 好 那它和E和F是没有调用关系 50 00:02:49,480 --> 00:02:52,760 它们俩可以在一组 E和F呢也是一样的 51 00:02:52,800 --> 00:02:54,200 所以它们仨一组 52 00:02:54,240 --> 00:02:56,160 这时候我怎么给它分配存储空间呢 53 00:02:56,200 --> 00:02:59,720 是每一组里头我按它最大的来分配 54 00:02:59,760 --> 00:03:02,480 那这时候呢我们分配完了是这样一种 55 00:03:02,520 --> 00:03:07,640 A给20K B和C呢一个50 一个30我给50 56 00:03:07,680 --> 00:03:15,320 剩下这个E F D这三个呢分别是30 20 40 我给40 57 00:03:15,360 --> 00:03:17,280 我分了这样几块之后 58 00:03:17,320 --> 00:03:20,560 那这时候它运行的时候会是啥样子呢 59 00:03:20,600 --> 00:03:23,240 刚开始运行的时候A B和D在内存里头 60 00:03:23,280 --> 00:03:26,200 它们之间运行 好运行到一段时间之后 61 00:03:26,240 --> 00:03:30,040 那开始C调用的时候调用E 62 00:03:30,080 --> 00:03:32,560 那这时候我把B和D换出去 63 00:03:32,600 --> 00:03:34,160 然后把C和E拿进来 64 00:03:34,200 --> 00:03:36,040 这时候在执行中间这一段 65 00:03:36,080 --> 00:03:38,120 假定说你是在做数据处理 66 00:03:38,160 --> 00:03:39,840 好 那它是可以运行的 67 00:03:39,880 --> 00:03:41,360 好 等处理完了之后 68 00:03:41,400 --> 00:03:43,800 那最后我要把结果输出来这是用的F 69 00:03:43,840 --> 00:03:46,080 这时候F交换过来 70 00:03:46,120 --> 00:03:49,440 因为这个F呢我们这一绺的大小 71 00:03:49,480 --> 00:03:52,200 是以它们三块最大来弄的 72 00:03:52,240 --> 00:03:54,760 所以这F放进来呢肯定是没问题的 73 00:03:54,800 --> 00:03:56,200 好 用这种做法之后 74 00:03:56,240 --> 00:03:58,360 我总共占用的存储空间是多少 75 00:03:58,400 --> 00:04:02,760 40 50 20加在一起110 那它是可以运行的 76 00:04:02,800 --> 00:04:05,480 好 那这样以来的话如果说你的系统里 77 00:04:05,520 --> 00:04:09,000 只有110K的物理内存 78 00:04:09,040 --> 00:04:11,760 那这个大一点的程序就没有办法运行 79 00:04:11,800 --> 00:04:14,680 通过这种改造之后 使用覆盖技术 80 00:04:14,720 --> 00:04:16,840 我写出来的程序跟你的功能是一样的 81 00:04:16,880 --> 00:04:20,480 但是这时候它可以在110K的内存上运行 82 00:04:20,520 --> 00:04:23,480 好 通过这种办法呢我可以 83 00:04:23,520 --> 00:04:26,000 减少一个应用程序内存占用量 84 00:04:26,040 --> 00:04:30,880 从而可以在小的 小内存的系统上运行 85 00:04:30,920 --> 00:04:34,480 那这时候问这种做法需要了解这个关系 86 00:04:34,520 --> 00:04:37,000 我现在这个画法是最优的吗 87 00:04:37,040 --> 00:04:40,200 那实际上在这儿我们可以给出另一种划分办法 88 00:04:40,240 --> 00:04:42,520 那A是独立一个模块 89 00:04:42,560 --> 00:04:46,640 但是第二步呢我把B原来是和C放在一起 90 00:04:46,680 --> 00:04:47,880 这俩差挺远 91 00:04:47,920 --> 00:04:52,200 好 那这时候呢我把C B E和F放在一起 92 00:04:52,240 --> 00:04:54,280 这两块呢它尺寸比较接近 93 00:04:54,320 --> 00:04:55,720 好 那这时候我给它50K 94 00:04:55,760 --> 00:04:59,520 而把最后一个呢C和D它俩放在一起 95 00:04:59,560 --> 00:05:01,640 它俩都是30 这时候搁30 96 00:05:01,680 --> 00:05:04,000 把这尺寸接近的放在一起之后呢 97 00:05:04,040 --> 00:05:06,440 相互之间没有调用关系放在一起 98 00:05:06,480 --> 00:05:08,760 好 这时候它合在一起 99 00:05:08,800 --> 00:05:12,440 这几个加在一起是100 这个会更小 100 00:05:12,480 --> 00:05:13,960 那是不是它是最小的 101 00:05:14,000 --> 00:05:16,560 如果说你要严格去来讨论这个问题 102 00:05:16,600 --> 00:05:19,040 那是相当复杂的 所以在这儿呢 103 00:05:19,080 --> 00:05:22,960 这个给程序员的开销是很大的 104 00:05:23,000 --> 00:05:25,760 那我们说在实际的系统里头呢 105 00:05:25,800 --> 00:05:27,880 在我们的DOS操作系统上 106 00:05:27,920 --> 00:05:31,080 这是历史上用过的 里头有个Turbo Pascal 107 00:05:31,120 --> 00:05:34,000 那这是它的集成开发环境 108 00:05:34,040 --> 00:05:35,480 使用Pascal语言的 109 00:05:35,520 --> 00:05:36,760 好 那在这个环境里呢 110 00:05:36,800 --> 00:05:40,280 它提供了这种Overlay的覆盖技术 111 00:05:40,320 --> 00:05:42,840 那有了这个支持之后 112 00:05:42,880 --> 00:05:45,040 那实际上它这里面是有一堆库支持 113 00:05:45,080 --> 00:05:48,360 你在这里头呢这个模块的换入换出 114 00:05:48,400 --> 00:05:51,200 和模块之间的关系的这种指定 115 00:05:51,240 --> 00:05:56,240 好 那这时候说它的开发难度是会增加的 116 00:05:56,280 --> 00:05:59,360 因为我要确定人首先要用程序员 117 00:05:59,400 --> 00:06:02,960 来对模块进行划分 划分完了之后 118 00:06:03,000 --> 00:06:06,440 还要确定它们之间的覆盖关系 119 00:06:06,480 --> 00:06:09,400 那这时候呢 我的编程难度是增加的 120 00:06:09,440 --> 00:06:14,320 与此同时我的执行时间呢也会有所增加 121 00:06:14,360 --> 00:06:16,640 那这时候呢需要 原来我们执行的时候 122 00:06:16,680 --> 00:06:19,880 是你把190K一块读入内存 123 00:06:19,920 --> 00:06:23,240 然后你就开始执行了 后面就没有开销了 124 00:06:23,280 --> 00:06:24,520 好 而在这儿呢 125 00:06:24,560 --> 00:06:27,160 我不但要在刚开始的时候我读入一部分 126 00:06:27,200 --> 00:06:28,000 那后来呢 127 00:06:28,040 --> 00:06:30,000 我还会把另一部分再读进来 128 00:06:30,040 --> 00:06:34,960 那这种呢就会导致你的执行时间会增强 129 00:06:35,000 --> 00:06:37,840 那这种做法呢我们觉得它会有问题 130 00:06:37,880 --> 00:06:41,720 好 另一种做法交换技术 131 00:06:41,760 --> 00:06:45,880 这种做法呢是增加正在运行的程序 132 00:06:45,920 --> 00:06:48,040 或者说需要运行程序的空间 133 00:06:48,080 --> 00:06:50,680 这和我们前面说那个问题不太一样 134 00:06:50,720 --> 00:06:55,080 说 我原来呢是一个程序的内存空间就不够用了 135 00:06:55,120 --> 00:06:57,040 现在这儿呢 实际上讨论的是说 136 00:06:57,080 --> 00:06:59,960 我一个程序你肯定要够用的 137 00:07:00,000 --> 00:07:02,320 然后我只是当前这个程序呢 138 00:07:02,360 --> 00:07:06,000 由于多道的程序运行使得 139 00:07:06,040 --> 00:07:09,080 另一个应用程序占用了内存空间 140 00:07:09,120 --> 00:07:10,520 使得它的空间不够 141 00:07:10,560 --> 00:07:13,840 它并不讨论一个程序在所有内存空间里 142 00:07:13,880 --> 00:07:15,920 用的时候它仍然不够的情况 143 00:07:15,960 --> 00:07:19,640 好 那在这儿说 我们怎么做呢 你把那些 144 00:07:19,680 --> 00:07:21,600 如果说你多个进程 同时在内存里头 145 00:07:21,640 --> 00:07:25,520 我把那些另一些进程就把它暂停 146 00:07:25,560 --> 00:07:27,040 并且放到外存里去 147 00:07:27,080 --> 00:07:29,800 这样的话我的空间不就够用了吗 148 00:07:29,840 --> 00:07:33,200 当前正在运行的或者说你需要运行的进程 149 00:07:33,240 --> 00:07:35,240 它的地址空间就增大了 150 00:07:35,280 --> 00:07:37,600 那这时候呢需要注意一条 151 00:07:37,640 --> 00:07:42,000 它换入换出的基本单位是整个进程 152 00:07:42,040 --> 00:07:44,720 那这个单位呢导致了不像我们刚才的 153 00:07:44,760 --> 00:07:47,360 覆盖我是程序内部的事 154 00:07:47,400 --> 00:07:51,360 好 然后 这时候有两个基本操作 一个是换出 155 00:07:51,400 --> 00:07:55,000 我把一个进程的整个地址空间保存到外存里去 156 00:07:55,040 --> 00:07:59,120 跟它对应着呢是换入 我把某个进程 157 00:07:59,160 --> 00:08:02,760 在外存当中某个进程读到内存里头来让它能运行 158 00:08:02,800 --> 00:08:06,160 那这是以进程为单位的交换技术 159 00:08:06,200 --> 00:08:10,080 这种做法呢我们在前面也有这样一个示意图说 160 00:08:10,120 --> 00:08:14,320 我在这里头两个进程 一个进程在内存里头 161 00:08:14,360 --> 00:08:19,600 一个进程在外面 那现在说我要想让它运行 162 00:08:19,640 --> 00:08:21,720 它需要的空间大 我就把它换出来 163 00:08:21,760 --> 00:08:24,440 然后把它换进去 然后这样的话 164 00:08:24,480 --> 00:08:26,960 它的空间呢就能够运行了 165 00:08:27,000 --> 00:08:29,400 如果说这空间足够的时候呢 166 00:08:29,440 --> 00:08:32,240 一半它也能进行的 我就可以让它俩在内存里头 167 00:08:32,280 --> 00:08:34,800 这样你 你这个交替运行的时候 168 00:08:34,840 --> 00:08:38,600 它的速度就很快 但这时候呢空间不够 169 00:08:38,640 --> 00:08:40,680 好 这是交换 170 00:08:40,720 --> 00:08:43,720 我们使用交换技术可以把一些 171 00:08:43,760 --> 00:08:47,000 暂停执行的进程放到外存里头去 172 00:08:47,040 --> 00:08:49,200 但这时候呢也会有一些麻烦 173 00:08:49,240 --> 00:08:51,440 就是说首先第一个我们遇到麻烦 174 00:08:51,480 --> 00:08:54,480 是说我在什么时候来进行交换 175 00:08:54,520 --> 00:08:57,920 那这时候呢通常情况下我在内存不够的时候 176 00:08:57,960 --> 00:09:00,640 或者有不够可能性的时候 177 00:09:00,680 --> 00:09:02,800 比如说我一个正在执行的进程 178 00:09:02,840 --> 00:09:05,080 它的内存空间不够用了 179 00:09:05,120 --> 00:09:09,080 这时候我必须把另外一些暂停执行的 180 00:09:09,120 --> 00:09:10,400 并且在内存里的 181 00:09:10,440 --> 00:09:14,160 把它整个进程地址空间兑换到外存当中 182 00:09:14,200 --> 00:09:16,000 这时候我这个可以扩大 183 00:09:16,040 --> 00:09:19,360 另一种情况是说我有一个进程要执行 184 00:09:19,400 --> 00:09:22,480 现在内存里可用的空闲空间不够用 185 00:09:22,520 --> 00:09:25,600 好 那这时候我就把暂停的另外一些进程呢 186 00:09:25,640 --> 00:09:28,640 倒到外存里面去 这时候它可以来运行 187 00:09:28,680 --> 00:09:29,760 那这是时机 188 00:09:29,800 --> 00:09:32,680 再有一个是交换区域的大小 189 00:09:32,720 --> 00:09:34,520 也就是说我倒到外存里头去 190 00:09:34,560 --> 00:09:37,800 放在外存里头这些进程映象 191 00:09:37,840 --> 00:09:39,240 它要占多大空间呢 192 00:09:39,280 --> 00:09:43,240 需要把所有的暂停的用户进程 193 00:09:43,280 --> 00:09:45,600 全部保存到外存当中 194 00:09:45,640 --> 00:09:48,520 这时候呢它是需要占用一定的存储空间的 195 00:09:48,560 --> 00:09:51,880 还有一个问题是说 我换入的时候 196 00:09:51,920 --> 00:09:55,880 那你是否能放回到原处呢 如果说不放回到原处 197 00:09:55,920 --> 00:10:02,040 我原来的函数调用或者说有跳转 这些你怎么办 198 00:10:02,080 --> 00:10:04,760 那这个时候呢我们说它的做法是 199 00:10:04,800 --> 00:10:08,040 需要采用动态的地址映射的办法 200 00:10:08,080 --> 00:10:13,720 而这些前面关于交换和覆盖的这种技术准备 201 00:10:13,760 --> 00:10:17,040 都为我们的虚拟存储打下基础 202 00:10:17,080 --> 00:10:22,120 好 那我们对它做一个比较 对于覆盖来说 203 00:10:22,160 --> 00:10:24,680 它是在程序内部模块之间的 204 00:10:24,720 --> 00:10:28,480 跟程序外边没关系 好 这时候它是一个进程 205 00:10:28,520 --> 00:10:31,200 在物理空间里运行它就不够了 206 00:10:31,240 --> 00:10:34,280 好 那这时候说我们需要兑换的呢 207 00:10:34,320 --> 00:10:36,640 覆盖进行交换的呢是这个 208 00:10:36,680 --> 00:10:39,040 没有调用关系的模块之间 209 00:10:39,080 --> 00:10:42,040 程序员必须知道这种逻辑覆盖关系 210 00:10:42,080 --> 00:10:43,360 这是比较麻烦的 211 00:10:43,400 --> 00:10:47,080 而对于交换来说呢它是以进程整个地址空间 212 00:10:47,120 --> 00:10:48,880 为单位来进行交换的 213 00:10:48,920 --> 00:10:51,920 这时候呢我们不需要这个逻辑关系 214 00:10:51,960 --> 00:10:54,520 它只会发生在进程之间 215 00:10:54,560 --> 00:10:58,040 好 这一部分呢实际上可以由操作系统来做的 216 00:10:58,080 --> 00:11:01,320 那上边这一部分可不可以由操作系统来做呢 217 00:11:01,360 --> 00:11:04,600 那这事有难度 原因在于这地方这种逻辑关系 218 00:11:04,640 --> 00:11:07,280 你操作系统没有办法很准确掌握的 219 00:11:07,320 --> 00:11:12,520 好 那这样的话是说 我有没有可能以使用 220 00:11:12,560 --> 00:11:14,720 由操作系统来做同时呢 221 00:11:14,760 --> 00:11:17,720 我又是不是以整个进程为单位 222 00:11:17,760 --> 00:11:19,880 是一部分一部分的 223 00:11:19,920 --> 00:11:21,560 进程地址空间的一部分内容 224 00:11:21,600 --> 00:11:22,880 我把它导入到外存里头去 225 00:11:22,920 --> 00:11:24,160 这个时候有没有可能呢 226 00:11:24,200 --> 00:11:26,040 那这就是我们下面要说到的 227 00:11:26,080 --> 00:11:29,120 虚拟存储要来做的事情 228 00:11:29,160 --> 00:11:29,880 229 00:11:29,920 --> 00:11:29,920