0 00:00:00,000 --> 00:00:07,000 1 00:00:07,160 --> 00:00:08,520 好 那我们接下来再看一下 2 00:00:08,640 --> 00:00:11,320 X86内存管理单元MMU 3 00:00:11,360 --> 00:00:12,880 那这一块为什么要跟大家讲这个 4 00:00:12,920 --> 00:00:14,640 是在于我们原理课讲到 5 00:00:14,680 --> 00:00:16,560 我们有对连续地址空间的管理和 6 00:00:16,600 --> 00:00:18,760 对离散地址空间的管理 7 00:00:18,800 --> 00:00:20,640 那么离散地址空间的管理需要 8 00:00:20,680 --> 00:00:22,360 所谓的一种段机制和页机制 9 00:00:22,400 --> 00:00:24,640 那么在X86里面正好 10 00:00:24,680 --> 00:00:26,520 这种CPU它两种机制都存在 11 00:00:26,560 --> 00:00:28,080 而且是可以共存的 12 00:00:28,120 --> 00:00:30,280 可以实现所谓的段页式的一种管理 13 00:00:30,320 --> 00:00:31,640 那我们可以看看X86 14 00:00:31,680 --> 00:00:35,000 怎么来有效的来建立这个段表 页表的 15 00:00:35,040 --> 00:00:36,560 那我们可以知道需要去 16 00:00:36,600 --> 00:00:38,920 进一步了解页表或者段表的一些格式 17 00:00:38,960 --> 00:00:41,240 以及如何操作页表项 18 00:00:41,280 --> 00:00:43,840 那么有了这些知识之后我们就可以 19 00:00:43,880 --> 00:00:48,680 在我们的硬件里面建立一套所谓的映射机制 20 00:00:48,720 --> 00:00:51,320 把我们段机制和页机制都建立起来 21 00:00:51,360 --> 00:00:54,560 完成对这种更大的一个空间的一个管理 22 00:00:54,600 --> 00:00:56,240 以及对离散空间的一个管理 23 00:00:56,280 --> 00:00:58,040 那么它的管理效率会更加高 24 00:00:58,080 --> 00:01:01,360 那这也是我们说CPU很重要的一个功能 25 00:01:01,400 --> 00:01:05,560 那这一块我们会讲解有关页表的一些信息 26 00:01:05,600 --> 00:01:08,040 以及怎么去建立好段表 页表 27 00:01:08,080 --> 00:01:11,000 以及怎么去操作页表项 28 00:01:11,040 --> 00:01:12,400 这是我们这一章讲的内容 29 00:01:12,440 --> 00:01:14,160 那么它和我们的lab2 30 00:01:14,200 --> 00:01:16,120 要完成的练习是一一对应的 31 00:01:16,160 --> 00:01:16,920 32 00:01:16,960 --> 00:01:18,720 首先我们看一下这个段机制 33 00:01:18,760 --> 00:01:21,280 其实在lab1里面我们已经接触到段机制 34 00:01:21,320 --> 00:01:24,040 只是我们没把重点放在那个地方去讲解 35 00:01:24,080 --> 00:01:25,480 而是把它留在lab2来讲解 36 00:01:25,520 --> 00:01:27,880 因为段和页它都属于 37 00:01:27,920 --> 00:01:30,280 我们的内存的管理的一套体系 38 00:01:30,320 --> 00:01:33,320 我们可以看着在X86 MMU里面 39 00:01:33,360 --> 00:01:35,000 它首先是有一套段机制 40 00:01:35,040 --> 00:01:36,400 这个段机制包含了 41 00:01:36,440 --> 00:01:39,720 比较多的一些功能的一些部件 42 00:01:39,760 --> 00:01:41,800 第一个是有一系列的寄存器 43 00:01:41,840 --> 00:01:44,800 我们说CS ES一直到GS 44 00:01:44,840 --> 00:01:48,000 第二个有段描述符 45 00:01:48,040 --> 00:01:51,560 那么寄存器里面的一些信息它的前面的 46 00:01:51,600 --> 00:01:56,840 最高端的十几位它是专门用来作为一个index 47 00:01:56,880 --> 00:02:01,120 作为一个索引来找到全局描述符表里面的一个项 48 00:02:01,160 --> 00:02:03,440 全局描述符表就是一个叫GDT 49 00:02:03,480 --> 00:02:05,400 可以理解为一个大的数组 50 00:02:05,440 --> 00:02:07,240 根据数组里面找到对应的数组项 51 00:02:07,280 --> 00:02:09,240 一项就是一个所谓的段描述符 52 00:02:09,280 --> 00:02:12,920 在段描述符里面表明了你要访问 53 00:02:12,960 --> 00:02:15,160 比如以CS为例 CS加上EIP 54 00:02:15,200 --> 00:02:17,960 它要访问的那个地址 对应的映射关系 55 00:02:18,000 --> 00:02:19,960 这里面有它的一个Base Address这是最主要的 56 00:02:20,000 --> 00:02:22,600 有了Base Address之后再加上 57 00:02:22,640 --> 00:02:25,000 把这个EIP作为Offset合在一起 58 00:02:25,040 --> 00:02:28,720 基址加上Offset形成最终的线性地址 59 00:02:28,760 --> 00:02:30,560 如果没有启动这个页机制的话 60 00:02:30,600 --> 00:02:33,000 那么线性地址就等同于物理地址 61 00:02:33,040 --> 00:02:37,480 从而可以完成前面由我们的CS ES 62 00:02:37,520 --> 00:02:42,680 以及CS加EIP ES或者是DS加上一个地址 63 00:02:42,720 --> 00:02:46,600 所形成的虚拟地址 到线性地址的一个转换 64 00:02:46,640 --> 00:02:48,720 这边是虚拟地址表示 65 00:02:48,760 --> 00:02:51,800 这边是最终的物理地址或者线性地址 66 00:02:51,840 --> 00:02:55,200 它中间通过我们段机制的段描述符 67 00:02:55,240 --> 00:02:58,160 来完成映射关系 段描述符里面 68 00:02:58,200 --> 00:03:02,080 重点是一个基址和它的一个段的限制 69 00:03:02,120 --> 00:03:04,080 这两块信息很重要 70 00:03:04,120 --> 00:03:05,160 我们有这两块信息之后 71 00:03:05,200 --> 00:03:07,520 就可以知道怎么完成这个转换 72 00:03:07,560 --> 00:03:10,000 那其实可以看出来这些信息 73 00:03:10,040 --> 00:03:11,880 是需要这些段描述信息 74 00:03:11,920 --> 00:03:14,120 这个CS里面它的index 75 00:03:14,160 --> 00:03:16,600 都需要我们的软件提前设置好 76 00:03:16,640 --> 00:03:17,800 这个软件是谁呢 77 00:03:17,840 --> 00:03:19,600 很明显就是我们的ucore操作系统 78 00:03:19,640 --> 00:03:22,480 79 00:03:22,520 --> 00:03:25,440 好 那有同学可能会提出质疑说 80 00:03:25,480 --> 00:03:28,480 你说的这个GDT它到底是 81 00:03:28,520 --> 00:03:30,640 放在内存里面的还是放在CPU里面的 82 00:03:30,680 --> 00:03:31,600 83 00:03:31,640 --> 00:03:32,520 大家想一想 84 00:03:32,560 --> 00:03:34,040 85 00:03:34,080 --> 00:03:36,880 其实GDT是放在内存里面的 为什么 86 00:03:36,920 --> 00:03:39,040 因为它占的空间比较大 87 00:03:39,080 --> 00:03:41,680 如果放在CPU的话 那么它这个开销太大 88 00:03:41,720 --> 00:03:43,040 对CPU的压力比较大 89 00:03:43,080 --> 00:03:45,120 所以说既然放在内存里面 90 00:03:45,160 --> 00:03:46,800 我每一次地址映射的时候 91 00:03:46,840 --> 00:03:51,040 都要去查所谓GDT全局描述符表 92 00:03:51,080 --> 00:03:52,560 简单我们也可以称之为段表 93 00:03:52,600 --> 00:03:54,800 访问这个段表 那其实开销很大 94 00:03:54,840 --> 00:03:55,960 我为了访问一个地址单元 95 00:03:56,000 --> 00:03:57,880 我要再去访问这个地址 96 00:03:57,920 --> 00:04:00,880 memory的速度远远小于我们CPU的速度 97 00:04:00,920 --> 00:04:02,560 这我们之前讲计算机原理课的时候 98 00:04:02,600 --> 00:04:03,800 会给大家提到的 99 00:04:03,840 --> 00:04:04,880 100 00:04:04,920 --> 00:04:05,800 那我们有什么办法 101 00:04:05,840 --> 00:04:07,760 来加快这个选择的速度呢 102 00:04:07,800 --> 00:04:09,120 其实靠我们的硬件来完成 103 00:04:09,160 --> 00:04:10,560 这一点跟软件没有关系 104 00:04:10,600 --> 00:04:14,080 那我们的硬件会把刚才我们的软件ucore 105 00:04:14,120 --> 00:04:16,040 建立在GDT里面那个 106 00:04:16,080 --> 00:04:18,120 段描述符里面的关键信息 107 00:04:18,160 --> 00:04:21,920 给放在一个特殊的位置 隐藏的部分 108 00:04:21,960 --> 00:04:23,800 这隐藏的部分就是我们几个寄存器 109 00:04:23,840 --> 00:04:25,800 CS SS一直到GS 110 00:04:25,840 --> 00:04:29,240 它这里面有一部分你看得见是16个bit 111 00:04:29,280 --> 00:04:32,320 那个段寄存器的值 但是还有一大段 112 00:04:32,360 --> 00:04:34,880 你是看不见的 是隐藏在后端 113 00:04:34,920 --> 00:04:36,200 我们的硬件直接控制 114 00:04:36,240 --> 00:04:39,160 它里面缓存了我们的基址 115 00:04:39,200 --> 00:04:41,040 还有限制等等其它一些信息 116 00:04:41,080 --> 00:04:44,560 这里面只关注两个 基址和它的段的一个大小 117 00:04:44,600 --> 00:04:46,920 那么有了这个信息之后 这个信息是放哪儿 118 00:04:46,960 --> 00:04:49,440 是放在我们的CPU里面的 119 00:04:49,480 --> 00:04:51,800 所以它可以通过CPU内部的一个访问 120 00:04:51,840 --> 00:04:53,040 就是我们说的MMU 121 00:04:53,080 --> 00:04:54,280 CPU内部的一个访问 122 00:04:54,320 --> 00:04:58,040 来加快整个段的一个映射的过程 123 00:04:58,080 --> 00:04:59,360 从而会提高效率 124 00:04:59,400 --> 00:05:00,640 125 00:05:00,680 --> 00:05:02,320 好我们可以看到这里面说 126 00:05:02,360 --> 00:05:05,480 通过这些操作在entry.S里面我们的ucore 127 00:05:05,520 --> 00:05:07,800 会建立好这个映射机制 128 00:05:07,840 --> 00:05:09,120 那么这是我们的操作系统 129 00:05:09,160 --> 00:05:10,400 建立的第一次映射机制 130 00:05:10,440 --> 00:05:12,680 另外需要注意的是它建立映射机制 131 00:05:12,720 --> 00:05:16,800 它完成了和我们前面讲的lab1所用到的 132 00:05:16,840 --> 00:05:18,240 bootloader的建立机制不太一样 133 00:05:18,280 --> 00:05:20,960 为什么这么说 因为lab1建立的是对等映射 134 00:05:21,000 --> 00:05:22,880 也就说我们的虚拟地址是0 135 00:05:22,920 --> 00:05:25,120 我们的物理地址我们线性地址也是0 136 00:05:25,160 --> 00:05:27,120 我们虚拟地址是1000 137 00:05:27,160 --> 00:05:28,960 那我们的物理地址也是1000 138 00:05:29,000 --> 00:05:30,320 它们完全是对等 139 00:05:30,360 --> 00:05:33,480 那么在lab2里面它和lab1有比较大的区别 140 00:05:33,520 --> 00:05:35,440 为什么这么说 因为link的时候 141 00:05:35,480 --> 00:05:37,560 它用到那个描述文件是 142 00:05:37,600 --> 00:05:41,920 实际上指出了它的偏移是0xC0000000 143 00:05:41,960 --> 00:05:47,480 且还是负的 为什么 这也是一个问题 144 00:05:47,520 --> 00:05:49,680 那也意味着如果这么来设置的话 145 00:05:49,720 --> 00:05:52,560 我们访问 146 00:05:52,600 --> 00:05:55,080 如果我们虚拟地址是0xC0000000的时候 147 00:05:55,120 --> 00:05:58,280 实际上对应的这个线性地址是0 148 00:05:58,320 --> 00:05:59,480 它们是这么一个映射关系 149 00:05:59,520 --> 00:06:03,600 就是虚拟地址比我们线性地址要大0xC0000000 150 00:06:03,640 --> 00:06:06,120 它这么一个映射 为什么这么映射 151 00:06:06,160 --> 00:06:07,400 我们后面会进一步讲到 152 00:06:07,440 --> 00:06:08,280 153 00:06:08,320 --> 00:06:10,360 刚才那页讲的是说 154 00:06:10,400 --> 00:06:13,560 操作系统ucore在开始执行的时候 155 00:06:13,600 --> 00:06:15,680 它建立的映射关系 156 00:06:15,720 --> 00:06:17,640 那其实我们再回顾一下lab1 157 00:06:17,680 --> 00:06:19,440 我们lab1里面在一开始 158 00:06:19,480 --> 00:06:22,680 它的bootloader也建立了一个映射关系 159 00:06:22,720 --> 00:06:26,200 那个映射关系是一个对等映射 160 00:06:26,240 --> 00:06:28,920 就0地址对应着0地址 1000对应着1000 161 00:06:28,960 --> 00:06:31,400 它的大致建立过程是类似的 162 00:06:31,440 --> 00:06:33,920 只是唯一的不同在哪儿 163 00:06:33,960 --> 00:06:37,800 就是它用到的那个映射的关系 164 00:06:37,840 --> 00:06:39,480 放在我们的GDT里面那个 165 00:06:39,520 --> 00:06:41,800 段描述符里面的信息是不一样的 166 00:06:41,840 --> 00:06:42,560 167 00:06:42,600 --> 00:06:47,680 这导致了它们的映射关系完全发生了变化 168 00:06:47,720 --> 00:06:49,680 我们现在是说我们希望 169 00:06:49,720 --> 00:06:52,160 我们的操作系统能够用上页机制 170 00:06:52,200 --> 00:06:54,320 那其实段是一种映射机制 171 00:06:54,360 --> 00:06:56,040 我们的页又是另外一种映射机制 172 00:06:56,080 --> 00:06:57,240 这时候有一个取舍 173 00:06:57,280 --> 00:06:58,680 到底是两个都充分利用 174 00:06:58,720 --> 00:07:00,360 还是强调其中一个 175 00:07:00,400 --> 00:07:04,280 而忽视或者说弱化另外一个映射机制 176 00:07:04,320 --> 00:07:06,120 大家想一想 我们在课上 177 00:07:06,160 --> 00:07:09,400 也对段机制和页机制做了一个讨论 178 00:07:09,440 --> 00:07:11,880 那我们可以看看到底选择哪个更加方便 179 00:07:11,920 --> 00:07:13,000 或者更加合适 180 00:07:13,040 --> 00:07:14,640 那么其实对我们硬件架构来说 181 00:07:14,680 --> 00:07:17,320 选择页机制相对来说有助于 182 00:07:17,360 --> 00:07:19,560 我们硬件机制对它进行有效的处理 183 00:07:19,600 --> 00:07:22,600 所以说你可以看到当前主流的CPU里面 184 00:07:22,640 --> 00:07:26,560 比如除了X86之外还有MIPS ARM PowerPC等等 185 00:07:26,600 --> 00:07:30,040 都采用了这种页机制为主的一种页映射关系 186 00:07:30,080 --> 00:07:32,520 所以说在这里面我们的ucore也是一样 187 00:07:32,560 --> 00:07:35,840 还需要通过段机制里面的安全保护手段 188 00:07:35,880 --> 00:07:37,640 来确保整个系统的安全 189 00:07:37,680 --> 00:07:39,560 但是我们弱化了它的映射机制 190 00:07:39,600 --> 00:07:42,520 怎么弱化 那既然我们说要选择页机制 191 00:07:42,560 --> 00:07:43,520 来完成这个映射 192 00:07:43,560 --> 00:07:46,000 就是从0xC0000000的虚地址 193 00:07:46,040 --> 00:07:48,880 映射到一个物理地址为0的这么一个过程 194 00:07:48,920 --> 00:07:51,320 那用页机制首先我们还是希望能够说 195 00:07:51,360 --> 00:07:53,240 把我们的段机制又恢复回去 196 00:07:53,280 --> 00:07:54,400 197 00:07:54,440 --> 00:07:55,760 为什么要恢复回去 198 00:07:55,800 --> 00:07:57,120 这个大家想一想 恢复到什么 199 00:07:57,160 --> 00:07:59,880 恢复到我们前面说的对等映射 200 00:07:59,920 --> 00:08:02,320 那它的过程是一样的 改变的是什么 201 00:08:02,360 --> 00:08:07,480 改变的仅仅是我们说的那个对段表 202 00:08:07,520 --> 00:08:09,440 全局描述符表里面的内容改变 203 00:08:09,480 --> 00:08:10,360 因为那个内容里面 204 00:08:10,400 --> 00:08:14,040 记录了每一个段的Base Address基址 205 00:08:14,080 --> 00:08:16,160 那个实际上是用来映射用的 206 00:08:16,200 --> 00:08:18,880 所以说那个做那么一个操作 207 00:08:18,920 --> 00:08:20,400 做这个操作 为什么要做这个操作 208 00:08:20,440 --> 00:08:22,160 是为了能够建立我们说的 209 00:08:22,200 --> 00:08:23,840 这个页机制做好一个准备 210 00:08:23,880 --> 00:08:24,840 否则我们的段机制 211 00:08:24,880 --> 00:08:27,400 它也要完成一个从0xC0000000 212 00:08:27,440 --> 00:08:29,400 到0的一个映射 这没必要 213 00:08:29,440 --> 00:08:31,960 214 00:08:32,000 --> 00:08:34,080 为什么这么麻烦 我还搞两次 215 00:08:34,120 --> 00:08:36,400 这一点也是留一个问题大家去考虑一下 216 00:08:36,440 --> 00:08:42,040 217 00:08:42,080 --> 00:08:42,560 218 00:08:42,600 --> 00:08:42,960 219 00:08:43,000 --> 00:08:43,040