0 00:00:00,000 --> 00:00:09,400 1 00:00:09,440 --> 00:00:11,400 练习六呢 主要是能够去 2 00:00:11,440 --> 00:00:13,520 建立中断向量表 3 00:00:13,560 --> 00:00:15,840 那这个相对来说复杂一点 4 00:00:15,880 --> 00:00:17,840 它需要知道向量表的这个细节 5 00:00:17,880 --> 00:00:19,960 就是一个中断向量表的一项 6 00:00:20,000 --> 00:00:24,040 到底应该是包含哪些中断描述符 7 00:00:24,080 --> 00:00:27,880 中断描述符到底是由哪些信息组成的 8 00:00:27,920 --> 00:00:30,080 可以看出来 在这里面呢 9 00:00:30,120 --> 00:00:36,160 跟中断相关的集中在这个地方 10 00:00:36,200 --> 00:00:36,960 我们说 11 00:00:37,000 --> 00:00:39,200 要让X86的中断系统能够正常工作 12 00:00:39,240 --> 00:00:41,440 那需要做几件事情 13 00:00:41,480 --> 00:00:58,560 这个事情 首先在初始化阶段能够看到 14 00:00:58,600 --> 00:01:03,200 pic_init 这是完成中断控制器的初始化 15 00:01:03,240 --> 00:01:04,920 这是一个外设 16 00:01:04,960 --> 00:01:06,280 是一个特殊的设备 17 00:01:06,320 --> 00:01:09,160 8259的一个中断控制器 18 00:01:09,200 --> 00:01:13,600 这里面有一些相关的细节 19 00:01:13,640 --> 00:01:14,720 这些细节也是主要是 20 00:01:14,760 --> 00:01:19,240 完成对8259一个管理和配制 21 00:01:19,280 --> 00:01:21,720 那么我们大致可以理解为 22 00:01:21,760 --> 00:01:23,680 把8259配置好之后 23 00:01:23,720 --> 00:01:26,520 相应的外设就能够产生中断 24 00:01:26,560 --> 00:01:28,000 并被CPU所接收和处理 25 00:01:28,040 --> 00:01:33,120 为接受后续的时钟中断打开一个基础 26 00:01:33,160 --> 00:01:35,880 那有了这个跟外设相关的 27 00:01:35,920 --> 00:01:37,840 中断控制器初始化完毕之后呢 28 00:01:37,880 --> 00:01:41,520 我们还需要让CPU建立好一个 29 00:01:41,560 --> 00:01:43,480 中断描述符表 30 00:01:43,520 --> 00:01:45,280 也或者简称为中断向量表 31 00:01:45,320 --> 00:01:47,360 那么中断描述符表呢 32 00:01:47,400 --> 00:01:49,240 就是这里面 叫IDT 33 00:01:49,280 --> 00:01:52,880 它的一个初始化过程 34 00:01:52,920 --> 00:01:58,760 那么IDT 我们在前面介绍基础知识的时候 35 00:01:58,800 --> 00:02:00,280 已经跟大家讲过 36 00:02:00,320 --> 00:02:02,920 就是80386这个中断描述符表的 37 00:02:02,960 --> 00:02:04,880 每一项代表什么意思 38 00:02:04,920 --> 00:02:08,480 这里面就把这个项建立好 39 00:02:08,520 --> 00:02:09,920 这里面这个索引 40 00:02:09,960 --> 00:02:11,880 这个中断描述符表可以理解为 41 00:02:11,920 --> 00:02:14,240 是一个大的数组 42 00:02:14,280 --> 00:02:16,280 每一个索引 能代表一个中断号 43 00:02:16,320 --> 00:02:18,480 当一个中断产生之后 44 00:02:18,520 --> 00:02:21,360 它会有对应这个中断号 45 00:02:21,400 --> 00:02:23,880 比如说时钟中断 它会有对应的中断号 46 00:02:23,920 --> 00:02:28,720 这个中断号会用来作为index 47 00:02:28,760 --> 00:02:32,200 来查IDT描述符表 48 00:02:32,240 --> 00:02:35,440 这个表里面对应index一项呢 49 00:02:35,480 --> 00:02:39,640 记录了当产生中断之后 50 00:02:39,680 --> 00:02:42,760 所要去应对中断服务例程的地址 51 00:02:42,800 --> 00:02:44,520 这个地址有两部分信息 52 00:02:44,560 --> 00:02:47,440 一部分是所谓段描述符 53 00:02:47,480 --> 00:02:48,960 或者叫段选择子 54 00:02:49,000 --> 00:02:52,440 第二部分是这个偏移 55 00:02:52,480 --> 00:02:55,360 可以根据段选择子中的内容 56 00:02:55,400 --> 00:03:00,480 来查找我们前面介绍过的段描述符 57 00:03:00,520 --> 00:03:03,960 段描述符存在什么地方呢 58 00:03:04,000 --> 00:03:06,720 段描述符存在全局描述符表中 59 00:03:06,760 --> 00:03:10,480 它一样 因为有了选择子这个index 60 00:03:10,520 --> 00:03:11,600 就可以基于这个index 61 00:03:11,640 --> 00:03:14,880 来查找段描述符 62 00:03:14,920 --> 00:03:16,840 查找段描述符之后 我们就知道 63 00:03:16,880 --> 00:03:21,480 这个有关这个中断服务例程的基址 64 00:03:21,520 --> 00:03:24,720 再加上它的offset 65 00:03:24,760 --> 00:03:28,360 就形成了中断服务例程的这个提示地址 66 00:03:28,400 --> 00:03:30,760 这个整个查找过程是由硬件完成的 67 00:03:30,800 --> 00:03:32,520 但是IDT这个表 68 00:03:32,560 --> 00:03:33,960 就中断描述符表 69 00:03:34,000 --> 00:03:36,560 和我们刚才说的全局描述符表 70 00:03:36,600 --> 00:03:38,240 是由我们软件来建立的 71 00:03:38,280 --> 00:03:43,240 这里面都是由uCore来建立的 72 00:03:43,280 --> 00:03:47,520 怎么能够让我们CPU知道说你建好这个表呢 73 00:03:47,560 --> 00:03:50,400 那这条特殊的指令 74 00:03:50,440 --> 00:03:52,160 比如LIDT 就是load idt 75 00:03:52,200 --> 00:03:55,960 就完成了对中断描述符表的加载 76 00:03:56,000 --> 00:03:58,920 从而使CPU知道中断描述符表在什么地方 77 00:03:58,960 --> 00:04:00,000 它就知道了 78 00:04:00,040 --> 00:04:09,680 这是它的一个起始地址 79 00:04:09,720 --> 00:04:12,560 当建立完这个8259 80 00:04:12,600 --> 00:04:14,800 这个中断控制器的初始化 81 00:04:14,840 --> 00:04:17,680 以及IDT中断描述符表初始化之后 82 00:04:17,720 --> 00:04:19,600 最后还需要一步 就是Enable中断 83 00:04:19,640 --> 00:04:25,600 就是使能中断 这个是在这儿 84 00:04:25,640 --> 00:04:26,880 叫intr enable 85 00:04:26,920 --> 00:04:28,520 如果说你某一个外设 86 00:04:28,560 --> 00:04:30,400 要想让它能够产生中断的话 87 00:04:30,440 --> 00:04:33,360 那么你对特定的外设也做相应的初始化 88 00:04:33,400 --> 00:04:35,000 所以说如果我们这里面 89 00:04:35,040 --> 00:04:36,480 是需要处理时钟中断的话 90 00:04:36,520 --> 00:04:39,400 那么时钟一个初始化过程也需要完成 91 00:04:39,440 --> 00:04:43,600 这里面呢 也是有很多跟外设相关的 92 00:04:43,640 --> 00:04:44,720 跟时钟外设相关的 93 00:04:44,760 --> 00:04:47,760 8253这个芯片相关的一些设置 94 00:04:47,800 --> 00:04:50,520 我们不用太追究细节 95 00:04:50,560 --> 00:04:55,480 可以大致理解为它每一百个tick 96 00:04:55,520 --> 00:04:56,800 会产生一次中断 97 00:04:56,840 --> 00:05:01,320 这里就是大致的一个介绍 98 00:05:01,360 --> 00:05:03,720 当完成了这个时钟外设的初始化之后呢 99 00:05:03,760 --> 00:05:05,040 有了这三步 100 00:05:05,080 --> 00:05:08,360 PIC IDT Clock这三步初始化 101 00:05:08,400 --> 00:05:10,800 那么我们就会使能中断 102 00:05:10,840 --> 00:05:12,280 使能中断就是一个很特殊的指令 103 00:05:12,320 --> 00:05:15,680 STI 这也是一条机器指令 104 00:05:15,720 --> 00:05:19,200 那么这条指令在什么地方呢 105 00:05:19,240 --> 00:05:21,760 STI其实也是一个内嵌汇编 106 00:05:21,800 --> 00:05:24,400 就是我们说的内联汇编 107 00:05:24,440 --> 00:05:26,200 ASM就是STI一条指令 108 00:05:26,240 --> 00:05:31,240 这条指令就完成了使能中断这么一个控制 109 00:05:31,280 --> 00:05:32,280 那么CLI呢 110 00:05:32,320 --> 00:05:33,440 实际上是另外一条指令 111 00:05:33,480 --> 00:05:35,600 就是屏蔽中断 112 00:05:35,640 --> 00:05:37,120 那其实有一个小问题 113 00:05:37,160 --> 00:05:40,040 一开始Bootloader启动的时候 114 00:05:40,080 --> 00:05:43,040 是处于中断使能 115 00:05:43,080 --> 00:05:44,800 还是中断是屏蔽的呢 116 00:05:44,840 --> 00:05:45,880 你怎么知道的呢 117 00:05:45,920 --> 00:05:52,600 这个问题大家可以考虑一下 118 00:05:52,640 --> 00:05:57,920 好 完成了这个中断向量表的初始化之后 119 00:05:57,960 --> 00:06:00,720 其实包括各种外设的初始化之后 120 00:06:00,760 --> 00:06:02,680 就可以产生中断了 121 00:06:02,720 --> 00:06:04,720 产生中断之后 122 00:06:04,760 --> 00:06:07,360 我们的中断服务例程怎么来响应它 123 00:06:07,400 --> 00:06:10,280 这个是需要进一步去完成的内容 124 00:06:10,320 --> 00:06:12,160 做实验内容需要去完成的 125 00:06:12,200 --> 00:06:13,040 那我们在这里面呢 126 00:06:13,080 --> 00:06:30,360 其实是在trap.c里面会有一个函数 127 00:06:30,400 --> 00:06:32,280 这个trap函数来接管 128 00:06:32,320 --> 00:06:34,400 但是比较有意思的一个问题是 129 00:06:34,440 --> 00:06:39,480 这个trap是中断服务例程的入口地址吗 130 00:06:39,520 --> 00:06:41,520 其实不是 131 00:06:41,560 --> 00:06:43,720 中断服务例程的入口地址 132 00:06:43,760 --> 00:06:48,720 在这个文件里面 叫vector.S 133 00:06:48,760 --> 00:06:50,560 这个汇编文件里面 134 00:06:50,600 --> 00:06:58,400 在这里面定义了255个中断号的 135 00:06:58,440 --> 00:07:00,400 所对应的起始地址 136 00:07:00,440 --> 00:07:01,880 当然我们这里面用不了这么多 137 00:07:01,920 --> 00:07:07,720 我们大致可以看着在前面有相应的一些处理 138 00:07:07,760 --> 00:07:08,840 那么它处理的入口 139 00:07:08,880 --> 00:07:11,000 都是叫__alltraps这个地方 140 00:07:11,040 --> 00:07:12,880 当产生64号中断的时候 141 00:07:12,920 --> 00:07:15,720 我们建立中断描述符表 142 00:07:15,760 --> 00:07:18,400 会使得我们CPU指针 143 00:07:18,440 --> 00:07:19,600 执行指令这个指针 144 00:07:19,640 --> 00:07:21,320 跳到EIP指到这个地方来 145 00:07:21,360 --> 00:07:23,120 vector64 这个地址 146 00:07:23,160 --> 00:07:25,520 会进一步跳到__alltraps里面 147 00:07:25,560 --> 00:07:36,720 __alltraps在trap entry里面 148 00:07:36,760 --> 00:07:38,080 这是__alltraps 149 00:07:38,120 --> 00:07:40,960 __alltraps会保存一系列的寄存器 150 00:07:41,000 --> 00:07:42,200 这里面就讲到了 151 00:07:42,240 --> 00:07:45,360 我们讲80386这个中断处理机制 152 00:07:45,400 --> 00:07:48,640 会讲到硬件产生中断之后会保存 153 00:07:48,680 --> 00:07:53,640 保存被打断的地址和它的flag寄存器等等 154 00:07:53,680 --> 00:07:56,680 但是它保存并不完整 那么为了能够 155 00:07:56,720 --> 00:07:59,440 回到被打断的地方重新执行呢 156 00:07:59,480 --> 00:08:01,120 我们需要确保 157 00:08:01,160 --> 00:08:04,640 环境能够完全恢复到跟以前一样 158 00:08:04,680 --> 00:08:06,560 所以我们把后续 159 00:08:06,600 --> 00:08:07,920 在执行中断服务例程中 160 00:08:07,960 --> 00:08:09,120 用到的寄存器的内容呢 161 00:08:09,160 --> 00:08:10,600 都要先保存起来 162 00:08:10,640 --> 00:08:12,880 以避免破坏返回去的那个环境 163 00:08:12,920 --> 00:08:15,560 这里面看到叫push DS ES等等 164 00:08:15,600 --> 00:08:18,520 这都是完成保存 165 00:08:18,560 --> 00:08:19,440 那这些保存信息都 166 00:08:19,480 --> 00:08:23,360 放在这个内核的中断站里面 167 00:08:23,400 --> 00:08:25,880 最后这里看到 168 00:08:25,920 --> 00:08:29,400 它这里面会有一个call trap 169 00:08:29,440 --> 00:08:32,680 那么这个call trap 实际上调用了一个函数 170 00:08:32,720 --> 00:08:37,240 这个函数实际上在trap.c里面 171 00:08:37,280 --> 00:08:38,800 就在这儿 172 00:08:38,840 --> 00:08:44,600 那么trap.c呢会进一步调trap dispatch 173 00:08:44,640 --> 00:08:49,520 那trap dispatch呢 174 00:08:49,560 --> 00:08:52,920 就会查找这个相应中断号 175 00:08:52,960 --> 00:08:55,840 如果说它发现是时钟中断 176 00:08:55,880 --> 00:08:57,560 这里面有这么一个标记 177 00:08:57,600 --> 00:09:00,040 时钟中断号需要注意这个 178 00:09:00,080 --> 00:09:01,800 加起来值就是32 179 00:09:01,840 --> 00:09:03,400 如果发现32 180 00:09:03,440 --> 00:09:07,200 它认为时钟中断会把这个ticks 181 00:09:07,240 --> 00:09:08,040 这是全局的变量 182 00:09:08,080 --> 00:09:09,600 把ticks做一个加操作 183 00:09:09,640 --> 00:09:12,760 如果ticks满了一定次数 184 00:09:12,800 --> 00:09:20,040 这里面设置的次数是100次吧 185 00:09:20,080 --> 00:09:21,440 那就会print一个ticks 186 00:09:21,480 --> 00:09:24,360 就打印出一个信息出来 187 00:09:24,400 --> 00:09:25,600 那这个信息出在什么地方呢 188 00:09:25,640 --> 00:09:27,080 这里很重要的结构 189 00:09:27,120 --> 00:09:29,880 就是trapframe 这个数据结构 190 00:09:29,920 --> 00:09:33,040 在这个结构里面 我们可以看到 191 00:09:33,080 --> 00:09:36,400 实际上它保存了被打断那一刻的 192 00:09:36,440 --> 00:09:39,080 很重要的寄存器的信息 193 00:09:39,120 --> 00:09:41,560 比如说下面这一段 194 00:09:41,600 --> 00:09:43,280 这一段是我们说到的 195 00:09:43,320 --> 00:09:47,400 是在这个硬件一旦产生中断之后 196 00:09:47,440 --> 00:09:51,720 我们硬件CPU会自动保存一些信息 197 00:09:51,760 --> 00:09:53,800 而接下来这些信息呢 198 00:09:53,840 --> 00:09:57,560 是我们的软件来保存的 199 00:09:57,600 --> 00:09:59,320 那么这一条信息是什么呢 200 00:09:59,360 --> 00:10:01,200 这条信息是考虑的是 201 00:10:01,240 --> 00:10:06,120 将来有可能出现从用户态产生中断 202 00:10:06,160 --> 00:10:08,600 会切换到内核态 203 00:10:08,640 --> 00:10:10,400 那么就会多保存一些信息 204 00:10:10,440 --> 00:10:12,520 就是ESP SS 205 00:10:12,560 --> 00:10:14,000 那么这条信息呢 206 00:10:14,040 --> 00:10:15,880 实际上是我们说 207 00:10:15,920 --> 00:10:18,400 如果出现不同特权级的转换 208 00:10:18,440 --> 00:10:20,400 比如说用户态产生中断之后 209 00:10:20,440 --> 00:10:21,760 会切到内核态 210 00:10:21,800 --> 00:10:23,680 那么这就出现一个特权级的转换 211 00:10:23,720 --> 00:10:25,720 那么它需要保存更多的一些信息 212 00:10:25,760 --> 00:10:28,280 要把用户态栈的信息给保存下来 213 00:10:28,320 --> 00:10:28,720 就保存这个地方 214 00:10:28,760 --> 00:10:31,320 以便于能够正确的恢复 215 00:10:31,360 --> 00:10:36,240 从内核态恢复到用户态去进一步执行 216 00:10:36,280 --> 00:10:37,600 对于X86而言 217 00:10:37,640 --> 00:10:42,800 用户态一般我们设置在特权级3 218 00:10:42,840 --> 00:10:54,920 而内核态设置在特权级0在这里面 219 00:10:54,960 --> 00:11:02,880 这是练习六大致的一个介绍 220 00:11:02,920 --> 00:11:06,000 扩展练习呢 其实是给一些 221 00:11:06,040 --> 00:11:08,080 觉得前面六个练习 222 00:11:08,120 --> 00:11:12,000 还觉得不够挑战的同学准备的 223 00:11:12,040 --> 00:11:16,160 主要是能够实现不同特权级的一个切换 224 00:11:16,200 --> 00:11:17,400 怎么能够通过中断机制 225 00:11:17,440 --> 00:11:19,720 来实现不同特权级的切换 226 00:11:19,760 --> 00:11:21,960 这里面重点就是要设置好 227 00:11:22,000 --> 00:11:26,640 返回到某一个特权级 228 00:11:26,680 --> 00:11:28,800 它所保存的信息 229 00:11:28,840 --> 00:11:32,320 保存那些寄存器的信息应该怎么设置 230 00:11:32,360 --> 00:11:34,080 这里面有很多小的细节 231 00:11:34,120 --> 00:11:38,280 如果有同学感兴趣的话可以进一步去学习 232 00:11:38,320 --> 00:11:42,120 那么我们就把练习一到扩展练习 233 00:11:42,160 --> 00:11:44,560 都给大家做了一个简单的介绍 234 00:11:44,600 --> 00:11:49,120 希望大家能够基于刚才介绍更好的完成实验 235 00:11:49,160 --> 00:11:53,520 有同学也说 这个一开始看代码量其实挺大的 236 00:11:53,560 --> 00:11:54,760 我们也给大家说到了 237 00:11:54,800 --> 00:11:55,920 就是关于每一个lab 238 00:11:55,960 --> 00:11:57,960 其实有一系列小的Project组成的 239 00:11:58,000 --> 00:12:00,560 如果大家比较好奇说 240 00:12:00,600 --> 00:12:04,800 想看到这个lab是怎么一步一步建立起来 241 00:12:04,840 --> 00:12:09,960 那么我们还可以查找相应的Project的信息 242 00:12:10,000 --> 00:12:11,240 我们也会在网上公布 243 00:12:11,280 --> 00:12:12,400 大家可以去看一看 244 00:12:12,440 --> 00:12:14,280 到底是如何一步一步的 245 00:12:14,320 --> 00:12:16,080 一个一个小的Project 246 00:12:16,120 --> 00:12:17,800 构成了一个大的lab 247 00:12:17,840 --> 00:12:20,120 然后最终形成一共8个lab 248 00:12:20,160 --> 00:12:23,080 那么它其实有几十个小的Project组成 249 00:12:23,120 --> 00:12:28,360 这是我们这个lab1的一个介绍 250 00:12:28,400 --> 00:12:30,000 好 谢谢大家 251 00:12:30,040 --> 00:12:30,080