0 00:00:00,000 --> 00:00:06,560 1 00:00:06,800 --> 00:00:11,640 我们刚才对内存管理的功能 2 00:00:11,720 --> 00:00:13,880 进行了一个基本的讨论 3 00:00:13,920 --> 00:00:20,720 接下来在讨论具体的内存管理算法之前 4 00:00:20,760 --> 00:00:22,640 我们有必要来讨论一下 5 00:00:22,680 --> 00:00:27,840 内存管理当中地址的生成 6 00:00:27,880 --> 00:00:32,240 我们说 从你写的程序里用到的符号 7 00:00:32,280 --> 00:00:35,800 到最后在总线上出现的物理地址 8 00:00:35,840 --> 00:00:39,520 这中间有一个转换的过程 9 00:00:39,560 --> 00:00:41,840 在具体说转换算法之前 10 00:00:41,880 --> 00:00:45,720 我们需要知道地址的生成过程 11 00:00:45,760 --> 00:00:49,440 和在生成过程当中并不是任何一个 12 00:00:49,480 --> 00:00:52,160 你想要访问的地址都是会允许你访问的 13 00:00:52,200 --> 00:00:54,240 这里还有一些安全的检查 14 00:00:54,280 --> 00:00:56,480 或者说合法性的检查在里头 15 00:00:56,520 --> 00:01:00,720 接下来我们看地址空间的定义 16 00:01:00,760 --> 00:01:04,760 我们在机器里总线上看到的地址 17 00:01:04,800 --> 00:01:07,920 是这里我们所说的 物理地址 18 00:01:07,960 --> 00:01:10,240 所有的物理地址空间所构成的空间 19 00:01:10,280 --> 00:01:11,920 叫做物理地址空间 20 00:01:11,960 --> 00:01:14,400 它是由硬件支持的 21 00:01:14,440 --> 00:01:15,720 通常情况下 比如说 22 00:01:15,760 --> 00:01:19,160 我们机器里说有多少位地址总线 23 00:01:19,200 --> 00:01:23,280 指的就是这里的物理地址总线的条数 24 00:01:23,320 --> 00:01:24,880 比如说32位的 25 00:01:24,920 --> 00:01:27,840 通常情况下就是32条地址线 26 00:01:27,880 --> 00:01:30,680 在我们这里它的编号是从0 27 00:01:30,720 --> 00:01:34,440 比如说32位 是0到4G减一 28 00:01:34,480 --> 00:01:37,880 那么这是从0开始一直到它最大编号 29 00:01:37,920 --> 00:01:42,960 这个编号在存储单元角度来讲是唯一的 30 00:01:43,000 --> 00:01:44,320 但是这种唯一 31 00:01:44,360 --> 00:01:46,640 实际上对于我们写程序来讲 32 00:01:46,680 --> 00:01:49,520 是不太容易来使用的 33 00:01:49,560 --> 00:01:51,680 因为我到底用哪个地址 34 00:01:51,720 --> 00:01:54,880 在程序写成之前 或者运行之前 35 00:01:54,920 --> 00:01:56,440 我可能是不知道的 36 00:01:56,480 --> 00:01:57,440 那么这样一来 37 00:01:57,480 --> 00:01:59,040 我们在这里用到第二个地址 38 00:01:59,080 --> 00:02:00,880 是逻辑地址 39 00:02:00,920 --> 00:02:03,240 逻辑地址是CPU运行的时候 40 00:02:03,280 --> 00:02:06,640 里边的进程看到的地址空间 41 00:02:06,680 --> 00:02:07,760 那通常情况下 42 00:02:07,800 --> 00:02:11,680 对应我们可执行文件里的那一段区域 43 00:02:11,720 --> 00:02:13,880 加载程序的时候 44 00:02:13,920 --> 00:02:18,360 你的程序加载到内存当中 它变成进程 45 00:02:18,400 --> 00:02:20,480 这个时候在你的可执行文件里的 46 00:02:20,520 --> 00:02:23,360 0到它最大值 47 00:02:23,400 --> 00:02:28,000 这个地方在相应地址空间里有一段区域 48 00:02:28,040 --> 00:02:32,360 这段区域就是我们进程的逻辑地址空间 49 00:02:32,400 --> 00:02:34,200 逻辑地址转换成物理地址 50 00:02:34,240 --> 00:02:38,040 就是我们后面会说到的方法 51 00:02:38,080 --> 00:02:42,000 那么这时我们这里访问到一条指令 52 00:02:42,040 --> 00:02:43,560 这条指令在执行的过程中 53 00:02:43,600 --> 00:02:47,280 它会访问相应的内存单元 54 00:02:47,320 --> 00:02:49,160 这些内存单元的地址从哪来 55 00:02:49,200 --> 00:02:51,240 就是从这 逻辑地址 56 00:02:51,280 --> 00:02:54,640 根据我们后面会说到方法 57 00:02:54,680 --> 00:02:56,480 转换成物理地址 58 00:02:56,520 --> 00:03:00,040 最后在总线上访问相应的存储单元 59 00:03:00,080 --> 00:03:03,800 我们在后边 一直说需要大家理解 60 00:03:03,840 --> 00:03:05,440 我到底用到逻辑地址 61 00:03:05,480 --> 00:03:07,280 物理地址分别是什么 62 00:03:07,320 --> 00:03:11,120 它们之间的转换过程是什么样子 63 00:03:11,160 --> 00:03:14,960 接下来我们看逻辑地址的生成 64 00:03:15,000 --> 00:03:17,240 大家在写程序的时候 通常情况下 65 00:03:17,280 --> 00:03:19,600 现在我们写程序都是用高级语言 66 00:03:19,640 --> 00:03:22,400 高级语言里头 这就是一个小例子 67 00:03:22,440 --> 00:03:26,640 一个程序 它有一个保留字 68 00:03:26,680 --> 00:03:28,160 prog到end 69 00:03:28,200 --> 00:03:29,920 这是它的开始和结束标志 70 00:03:29,960 --> 00:03:31,920 然后中间我调用了一个函数 71 00:03:31,960 --> 00:03:34,440 这个函数就是一个地址 72 00:03:34,480 --> 00:03:36,440 我们通常在写函数的时候 73 00:03:36,480 --> 00:03:40,320 里头你不会写 比如说0X多少多少 74 00:03:40,360 --> 00:03:42,280 作为你的函数的名字 75 00:03:42,320 --> 00:03:43,400 这个很不容易记 76 00:03:43,440 --> 00:03:45,560 我们会用一个符号来表示 77 00:03:45,600 --> 00:03:48,480 用符号之后 不同的函数之间 78 00:03:48,520 --> 00:03:49,160 这些符号之间 79 00:03:49,200 --> 00:03:52,920 它们就没有一个先后顺序的关系 80 00:03:52,960 --> 00:03:53,880 那你放到内存里头 81 00:03:53,920 --> 00:03:55,560 我可以把它放到任何一个位置 82 00:03:55,600 --> 00:03:58,680 这是我们在写程序源代码的时候 83 00:03:58,720 --> 00:04:00,600 你所希望见到状态 84 00:04:00,640 --> 00:04:03,480 然后这个源代码我们就会进行一次编译 85 00:04:03,520 --> 00:04:07,080 源代码里的这些语句 86 00:04:07,120 --> 00:04:09,880 我们的CPU是没办法直接认识 87 00:04:09,920 --> 00:04:12,440 我们为了让CPU能认识 88 00:04:12,480 --> 00:04:14,920 必须转化成CPU能认识的指令 89 00:04:14,960 --> 00:04:16,920 指令我们转换出来的第一步 90 00:04:16,960 --> 00:04:20,640 是把它转变成机器能认识的指令的汇编码 91 00:04:20,680 --> 00:04:22,400 这是汇编指令 92 00:04:22,440 --> 00:04:23,680 这样转换过来之后 93 00:04:23,720 --> 00:04:26,360 我们看到它会转变成函数调用 94 00:04:26,400 --> 00:04:28,520 jmp或者是call 95 00:04:28,560 --> 00:04:30,600 都是会有的 96 00:04:30,640 --> 00:04:33,960 然后后面仍然用的是符号名字 97 00:04:34,000 --> 00:04:37,640 这还只是汇编的源代码 98 00:04:37,680 --> 00:04:40,400 那我们通过编译之后得到汇编码 99 00:04:40,440 --> 00:04:45,560 然后我们会再对它进行一次汇编 100 00:04:45,600 --> 00:04:49,480 汇编之后实际上我们就变成了二进制代码了 101 00:04:49,520 --> 00:04:53,640 这个时候就是实实在在是机器能认识的指令 102 00:04:53,680 --> 00:04:58,720 这个时候里头的符号就不能再是 103 00:04:58,760 --> 00:05:01,480 我们前面讲的这些字符串了 104 00:05:01,520 --> 00:05:04,520 而必须是地址空间里的某一个位置 105 00:05:04,560 --> 00:05:06,280 比如这个地方就是75 106 00:05:06,320 --> 00:05:08,200 那75 这是从0 107 00:05:08,240 --> 00:05:10,840 假定这个长度是蹦到这个位置 108 00:05:10,880 --> 00:05:13,280 那么就是你在当前的位置蹦到75 109 00:05:13,320 --> 00:05:15,240 这个地方用到的就是编号 110 00:05:15,280 --> 00:05:17,320 这是一个编号实际上是 111 00:05:17,360 --> 00:05:21,600 在这里头我可能会用到别的符号 112 00:05:21,640 --> 00:05:22,760 别的地址 113 00:05:22,800 --> 00:05:23,760 比如我有一个函数调用 114 00:05:23,800 --> 00:05:27,640 从模块A调用模块B里的一个函数 115 00:05:27,680 --> 00:05:29,680 在这个调用过程中 116 00:05:29,720 --> 00:05:32,560 在你做汇编的时候 117 00:05:32,600 --> 00:05:36,160 另一个模块的位置你并不知道 118 00:05:36,200 --> 00:05:38,880 这时我需要再有一个链接的过程 119 00:05:38,920 --> 00:05:41,080 把多个模块和你用到的函数库 120 00:05:41,120 --> 00:05:44,560 搁在一起 把它排成一个线性的序列 121 00:05:44,600 --> 00:05:45,960 排了之后这个时候我就知道 122 00:05:46,000 --> 00:05:48,680 你跳转的另一个符号的位置在哪 123 00:05:48,720 --> 00:05:50,640 比如说我们在这里头你自己会移动 124 00:05:50,680 --> 00:05:52,400 模块之间也会有 125 00:05:52,440 --> 00:05:53,360 那这个时候我会告诉你 126 00:05:53,400 --> 00:05:55,000 这个地方蹦完之后 127 00:05:55,040 --> 00:05:56,600 我放的位置往后挪了 128 00:05:56,640 --> 00:05:58,160 前面是放的函数库 129 00:05:58,200 --> 00:06:01,840 所以我起头的位置往后挪了100 130 00:06:01,880 --> 00:06:04,760 那我这变成了175 131 00:06:04,800 --> 00:06:07,480 这个175实际上就是从0开始 132 00:06:07,520 --> 00:06:09,640 我只是在这一个文件内的 133 00:06:09,680 --> 00:06:12,000 如果说我的程序这个时候去运行 134 00:06:12,040 --> 00:06:14,400 那么它运行的时候放到什么地方 135 00:06:14,440 --> 00:06:16,760 不一定能正好放到0的位置 136 00:06:16,800 --> 00:06:18,800 那这个时候我在加载的时候 137 00:06:18,840 --> 00:06:21,560 还会再有一个重定位 138 00:06:21,600 --> 00:06:24,080 这个重定位是说我原先的0 139 00:06:24,120 --> 00:06:25,600 这是175 140 00:06:25,640 --> 00:06:27,360 现在我加载进来之后 141 00:06:27,400 --> 00:06:29,720 我把它放到1000的位置 142 00:06:29,760 --> 00:06:34,080 那这1000呢 从1000到1175 143 00:06:34,120 --> 00:06:35,880 这个时候我的跳转 144 00:06:35,920 --> 00:06:38,240 我原来蹦到175 145 00:06:38,280 --> 00:06:40,560 你蹦的位置就错了 146 00:06:40,600 --> 00:06:43,640 我要统一把这个要平移一遍 147 00:06:43,680 --> 00:06:45,120 这我就变成1175 148 00:06:45,160 --> 00:06:47,480 这是你在加载时候的 149 00:06:47,520 --> 00:06:48,640 由操作系统提供的 150 00:06:48,680 --> 00:06:51,200 重定位的功能要干的事情 151 00:06:51,240 --> 00:06:52,760 有了这个之后 152 00:06:52,800 --> 00:06:54,960 我们的程序在跑的时候 153 00:06:55,000 --> 00:07:00,200 它就变成是实实在在的地址了 154 00:07:00,240 --> 00:07:03,160 这是逻辑地址 155 00:07:03,200 --> 00:07:05,760 然后 我们在获取这个地址的时候 156 00:07:05,800 --> 00:07:07,880 可以在什么时候来做到 157 00:07:07,920 --> 00:07:10,880 刚才做的是直接加载的时候 158 00:07:10,920 --> 00:07:15,120 但实际上地址生成机会有这样几个情况 159 00:07:15,160 --> 00:07:17,160 第一个是编译 160 00:07:17,200 --> 00:07:19,480 假定我知道我最后要放的位置 161 00:07:19,520 --> 00:07:23,200 我在编译的时候就可以把这个地址写死 162 00:07:23,240 --> 00:07:26,520 如果是这种情况 你的起始地址发生变化 163 00:07:26,560 --> 00:07:28,520 你就必须重新编译了 164 00:07:28,560 --> 00:07:31,040 这种情况现在在什么地方出现 165 00:07:31,080 --> 00:07:32,400 像我们用到手机 166 00:07:32,440 --> 00:07:34,520 如果说你的手机是功能手机的话 167 00:07:34,560 --> 00:07:36,000 不是智能手机 168 00:07:36,040 --> 00:07:38,520 这个时候里面的程序通常情况是写死的 169 00:07:38,560 --> 00:07:40,200 不允许你买了手机之后 170 00:07:40,240 --> 00:07:42,640 自己再往里装地址本了 171 00:07:42,680 --> 00:07:44,840 或者说装软件之类的 172 00:07:44,880 --> 00:07:46,920 那么这个时候通常情况下 173 00:07:46,960 --> 00:07:48,840 在前面这些都是写死的 174 00:07:48,880 --> 00:07:54,200 还有一种情况是允许你加载到不同地方 175 00:07:54,240 --> 00:07:56,000 比如说像我们现在的智能手机 176 00:07:56,040 --> 00:07:58,600 那你就可以在买到这个手机之后 177 00:07:58,640 --> 00:08:01,080 我再往里加我的程序 178 00:08:01,120 --> 00:08:03,920 这个时候写程序的人就没办法知道 179 00:08:03,960 --> 00:08:05,720 你这个程序最后会加载到 180 00:08:05,760 --> 00:08:08,160 你的系统里的什么地方去 181 00:08:08,200 --> 00:08:10,920 如果是这种情况 我在加载的时候 182 00:08:10,960 --> 00:08:13,480 就必须做重定位 183 00:08:13,520 --> 00:08:16,880 也就是说我根据我装到内存位置里的不同 184 00:08:16,920 --> 00:08:20,520 我要把里头那些符号的名字或者跳转的地址 185 00:08:20,560 --> 00:08:22,320 把它重新捋一遍 186 00:08:22,360 --> 00:08:26,440 通常情况下在我们的可执行文件里头 187 00:08:26,480 --> 00:08:30,160 它前面有一个重定位表 188 00:08:30,200 --> 00:08:32,280 那这个重定项目表 189 00:08:32,320 --> 00:08:33,280 里头包含内容就是 190 00:08:33,320 --> 00:08:34,320 你在这个程序里头 191 00:08:34,360 --> 00:08:36,600 到底哪些地方需要去改的 192 00:08:36,640 --> 00:08:39,000 加载的时候把这个都改成绝对地址 193 00:08:39,040 --> 00:08:41,160 那你的程序就可以跑了 194 00:08:41,200 --> 00:08:43,600 这是我们刚才见的情况 195 00:08:43,640 --> 00:08:48,160 还有一种情况 是执行时生成 196 00:08:48,200 --> 00:08:50,400 这个相当于我们在前面用的 197 00:08:50,440 --> 00:08:54,160 一直就是相对地址 198 00:08:54,200 --> 00:08:55,880 那么到执行的时候 199 00:08:55,920 --> 00:08:57,640 执行到这条指令的时候 200 00:08:57,680 --> 00:09:01,840 我才可以去知道它确切访问的是什么地方 201 00:09:01,880 --> 00:09:04,480 这种情况出现在 202 00:09:04,520 --> 00:09:07,400 我们使用虚拟存储的系统里头 203 00:09:07,440 --> 00:09:09,520 也就是说我执行一条指令 204 00:09:09,560 --> 00:09:11,960 这条指令访问的位置 访问到那之后 205 00:09:12,000 --> 00:09:15,640 有可能你当时把这一块区域 206 00:09:15,680 --> 00:09:17,720 放到内存的某一个位置 207 00:09:17,760 --> 00:09:20,880 这个时候它有一个映射 208 00:09:20,920 --> 00:09:23,120 映射过去之后你找到相应的位置 209 00:09:23,160 --> 00:09:25,520 那这个时候只是在执行这条指令的时候 210 00:09:25,560 --> 00:09:27,400 才会做这种映射 211 00:09:27,440 --> 00:09:29,400 这样做就有一个什么样的好处 212 00:09:29,440 --> 00:09:32,120 我这个程序在执行的过程当中 213 00:09:32,160 --> 00:09:34,680 我就可以把它所在的位置 214 00:09:34,720 --> 00:09:37,280 在物理存储的位置 我可以挪 215 00:09:37,320 --> 00:09:39,240 而如果说是前面两种情况的话 216 00:09:39,280 --> 00:09:43,320 你不但要求你在地址空间是连续的 217 00:09:43,360 --> 00:09:46,640 同时在这里头 它运行起来之后 218 00:09:46,680 --> 00:09:48,280 你是不能再动它的 219 00:09:48,320 --> 00:09:51,440 比如说在这里头我加载的时候做了重定位 220 00:09:51,480 --> 00:09:52,960 我已经写了绝对地址了 221 00:09:53,000 --> 00:09:55,000 由于你存储空间不够 222 00:09:55,040 --> 00:09:56,880 或者说别的程序的存储空间不够 223 00:09:56,920 --> 00:09:59,400 你把它位置往后挪了一段 224 00:09:59,440 --> 00:10:02,440 你这么一挪完之后 你那些位置就不对了 225 00:10:02,480 --> 00:10:05,560 所以从灵活性的角度来讲 226 00:10:05,600 --> 00:10:08,680 我们在执行时候生成这个地址是最好的 227 00:10:08,720 --> 00:10:12,000 而前面几种它有一个好处是简单 228 00:10:12,040 --> 00:10:14,200 所以在这里 不同系统里这几种做法 229 00:10:14,240 --> 00:10:16,160 我们现在都是有采用的 230 00:10:16,200 --> 00:10:19,680 下面我们通过一个例子 图示 231 00:10:19,720 --> 00:10:22,240 来看一下地址的生成过程 232 00:10:22,280 --> 00:10:26,040 这是我们在前面已说到过的系统结构 233 00:10:26,080 --> 00:10:28,400 CPU 内存 I/O设备 234 00:10:28,440 --> 00:10:30,480 我们一条指令的执行 235 00:10:30,520 --> 00:10:31,600 比如说在这里头 236 00:10:31,640 --> 00:10:33,920 CPU当前正在执行一条指令 237 00:10:33,960 --> 00:10:36,240 这条指令是movl指令 238 00:10:36,280 --> 00:10:40,440 这条指令在执行的时候里头有地址 239 00:10:40,480 --> 00:10:43,200 这个地址在CPU里先看到了 240 00:10:43,240 --> 00:10:46,480 然后这个时候我的MMU 241 00:10:46,520 --> 00:10:49,200 它依据这边的页表 242 00:10:49,240 --> 00:10:55,320 来把你这边见到地址翻译成物理地址 243 00:10:55,360 --> 00:10:57,840 翻译成物理地址之后 244 00:10:57,880 --> 00:11:01,240 然后CPU里头有一个控制器 245 00:11:01,280 --> 00:11:05,520 这个控制器负责把你得到的物理地址 246 00:11:05,560 --> 00:11:10,120 和相关总线控制信号送到总线上去 247 00:11:10,160 --> 00:11:16,280 这个时候存储单元 存储芯片 248 00:11:16,320 --> 00:11:19,360 这个时候会识别总线上地址和控制信号 249 00:11:19,400 --> 00:11:23,920 依据你控制信号的 到底是读还是写 250 00:11:23,960 --> 00:11:27,880 总线上有一组相应的持续逻辑的交互 251 00:11:27,920 --> 00:11:31,640 如果是写 就会把这边CPU送过来的数据 252 00:11:31,680 --> 00:11:35,760 写到内存当中 指定的存储单元上 253 00:11:35,800 --> 00:11:40,080 如果读那就从指定的内存单元当中 254 00:11:40,120 --> 00:11:42,520 读出数据 放到数据总线上 255 00:11:42,560 --> 00:11:44,880 然后CPU拿回去 256 00:11:44,920 --> 00:11:47,520 这是它的一个交互过程 257 00:11:47,560 --> 00:11:50,800 在这个交互过程当中 CPU能干什么呢 258 00:11:50,840 --> 00:11:53,040 CPU能干的是 259 00:11:53,080 --> 00:11:55,720 地址转换过程它的影响 260 00:11:55,760 --> 00:11:58,440 实际上在每一次访问的时候 261 00:11:58,480 --> 00:12:01,600 它是不依赖于软件的 262 00:12:01,640 --> 00:12:03,760 是由硬件来完成这个转换的 263 00:12:03,800 --> 00:12:05,440 但这个转换的表 264 00:12:05,480 --> 00:12:07,240 我们是可以通过操作系统 265 00:12:07,280 --> 00:12:09,600 来建立这两者之间的关系 266 00:12:09,640 --> 00:12:14,440 这是我们后边会说到的页表的功劳 267 00:12:14,480 --> 00:12:15,840 接下来我们讨论 268 00:12:15,880 --> 00:12:18,720 在地址生成过程当中的地址检查 269 00:12:18,760 --> 00:12:21,160 这里是一个图示 270 00:12:21,200 --> 00:12:23,840 说明我们CPU在执行指令的时候 271 00:12:23,880 --> 00:12:28,000 它的地址处理过程 生成过程 272 00:12:28,040 --> 00:12:29,800 这是一条movl指令 273 00:12:29,840 --> 00:12:33,000 movl指令在CPU执行过程当中 274 00:12:33,040 --> 00:12:35,960 它会产生逻辑地址 275 00:12:36,000 --> 00:12:37,160 这个逻辑地址 276 00:12:37,200 --> 00:12:41,400 比如我访问的是数据段的数据 277 00:12:41,440 --> 00:12:44,000 那好 这时候数据段 278 00:12:44,040 --> 00:12:47,200 它有一个段基址和段的长度 279 00:12:47,240 --> 00:12:49,760 如果说你从数据段去访问的 280 00:12:49,800 --> 00:12:52,360 偏移量超过这个长度 281 00:12:52,400 --> 00:12:55,600 这个时候的这个访问应该是非法的 282 00:12:55,640 --> 00:12:58,840 对于这种情况 在每次访问的时候 283 00:12:58,880 --> 00:13:00,040 每一条指令在访问的时候 284 00:13:00,080 --> 00:13:05,200 它都会去检查你的段的长度和偏移量 285 00:13:05,240 --> 00:13:08,000 是不是有效的范围 286 00:13:08,040 --> 00:13:09,960 如果不是 287 00:13:10,000 --> 00:13:11,840 那么这个时候就走上面这一条 288 00:13:11,880 --> 00:13:14,120 告诉你内存访问异常 289 00:13:14,160 --> 00:13:16,240 这条指令执行失败 290 00:13:16,280 --> 00:13:17,560 进行相应的错误处理 291 00:13:17,600 --> 00:13:18,840 这个由操作系统来做 292 00:13:18,880 --> 00:13:20,360 另一种情况是说 293 00:13:20,400 --> 00:13:21,920 这里检查完的结果 294 00:13:21,960 --> 00:13:23,840 你访问的偏移量 295 00:13:23,880 --> 00:13:27,000 是在0和你的最大长度之间 296 00:13:27,040 --> 00:13:29,400 我认为这是合理的 是合法的 297 00:13:29,440 --> 00:13:32,520 这个时候它会和段基址加在一起 298 00:13:32,560 --> 00:13:33,840 得到你的物理地址 299 00:13:33,880 --> 00:13:35,840 比如说在这500 1000 300 00:13:35,880 --> 00:13:36,920 那就是1500 301 00:13:36,960 --> 00:13:39,720 从这访问到你对应进程的 302 00:13:39,760 --> 00:13:44,240 物理地址空间里去 303 00:13:44,280 --> 00:13:46,520 那在这个过程中我们说 304 00:13:46,560 --> 00:13:50,760 操作系统可以通过用指令来 305 00:13:50,800 --> 00:13:54,760 设置相应的长度和段基址 306 00:13:54,800 --> 00:13:57,720 这是会我们可以通过软件方法来 307 00:13:57,760 --> 00:14:00,640 影响到我这里做相应检查 308 00:14:00,680 --> 00:14:03,800 有了这个检查之后我们就有了 309 00:14:03,840 --> 00:14:09,280 从符号一直到你的逻辑地址 310 00:14:09,320 --> 00:14:12,600 逻辑地址在执行过程当中转变成物理地址 311 00:14:12,640 --> 00:14:17,880 并且在这个过程中有相应的检查机制 312 00:14:17,920 --> 00:14:23,760 这是我们在这里说到地址的生成和检查 313 00:14:23,800 --> 00:14:23,840