0 00:00:00,000 --> 00:00:06,360 1 00:00:06,400 --> 00:00:08,560 好 那我们看看当产生了 2 00:00:08,600 --> 00:00:10,360 一个页访问异常的时候 3 00:00:10,400 --> 00:00:12,240 我们接下来ucore应该怎么处理 4 00:00:12,280 --> 00:00:14,840 首先可以看看一旦产生异常 5 00:00:14,880 --> 00:00:17,800 会出现所谓的一个异常的中断 6 00:00:17,840 --> 00:00:19,920 有个中断号 根据我们lab1 7 00:00:19,960 --> 00:00:22,400 中断这个建立中断处理机制呢 8 00:00:22,440 --> 00:00:24,520 一个中断号会落到相应的 9 00:00:24,560 --> 00:00:27,760 中断服务例程里面的入口地址里面去 10 00:00:27,800 --> 00:00:29,440 而对于缺页异常来说 11 00:00:29,480 --> 00:00:31,640 它是十进制的14 12 00:00:31,680 --> 00:00:35,400 那么14呢会跳到这个vector14这个地方 13 00:00:35,440 --> 00:00:37,600 这是一个地址 去继续执行 14 00:00:37,640 --> 00:00:39,480 这个地址位于这个vector.S 15 00:00:39,520 --> 00:00:41,040 这个汇编文件里面 16 00:00:41,080 --> 00:00:44,720 好 跳到这儿之后呢 从这个地方开始 17 00:00:44,760 --> 00:00:46,960 进一步会跳到_alltraps是一个汇总 18 00:00:47,000 --> 00:00:49,800 所有的这个中断一个入口的地方 19 00:00:49,840 --> 00:00:52,160 就_alltraps还是在trap_entry.s 20 00:00:52,200 --> 00:00:54,440 这个文件它会还有一个就是 21 00:00:54,480 --> 00:00:55,680 最后你处理完中断之后 22 00:00:55,720 --> 00:00:56,920 还要返回的一个地方 23 00:00:56,960 --> 00:01:00,160 也在这个地方来进行进一步的处理 24 00:01:00,200 --> 00:01:04,280 好_alltraps会调这个trap 25 00:01:04,320 --> 00:01:07,280 这个trap实际上到了c程序里面去了 26 00:01:07,320 --> 00:01:10,880 我们trap.c里面来完成进一步的 27 00:01:10,920 --> 00:01:17,760 跟所有需要特别关注的中断的处理 28 00:01:17,800 --> 00:01:19,200 那么trap_dispatch 29 00:01:19,240 --> 00:01:21,720 是它的进一步调用的函数 30 00:01:21,760 --> 00:01:23,480 好trap_dispatch 31 00:01:23,520 --> 00:01:25,720 最终会调到pgfault_handler 32 00:01:25,760 --> 00:01:28,320 这实际上是我们缺页中断处理例程 33 00:01:28,360 --> 00:01:31,480 这个都位于我们trap.c这个地方 34 00:01:31,520 --> 00:01:32,560 大家可以去关注一下 35 00:01:32,600 --> 00:01:34,120 这三个函数的实现 36 00:01:34,160 --> 00:01:36,400 那么pgfault_handler 37 00:01:36,440 --> 00:01:38,880 在一开始可以简单的去完成 38 00:01:38,920 --> 00:01:40,400 所谓的一个print ok 39 00:01:40,440 --> 00:01:41,680 当产生缺页异常的时候 40 00:01:41,720 --> 00:01:44,960 你这边能正常接收到这个相应的中断 41 00:01:45,000 --> 00:01:48,520 相应的中断 那这个过程是打通了 42 00:01:48,560 --> 00:01:51,440 但是光打通过程之后呢还是不够的 43 00:01:51,480 --> 00:01:52,760 我们知道只能说明 44 00:01:52,800 --> 00:01:55,200 页访问异常能够捕获住了 45 00:01:55,240 --> 00:01:57,040 但对于我们虚存管理来说 46 00:01:57,080 --> 00:01:58,600 捕获住这个页访问异常之后 47 00:01:58,640 --> 00:02:00,400 你还要做进一步的处理 48 00:02:00,440 --> 00:02:01,920 进一步的处理来确保 49 00:02:01,960 --> 00:02:03,400 如果是合法页的访问 50 00:02:03,440 --> 00:02:05,200 你应该能够有效应对这种情况 51 00:02:05,240 --> 00:02:08,760 来给它完成正确的读和写 52 00:02:08,800 --> 00:02:11,000 如果是一个不合法的这个页的访问 53 00:02:11,040 --> 00:02:12,600 那其实你应该报错 54 00:02:12,640 --> 00:02:13,920 这是不同的处理方式 55 00:02:13,960 --> 00:02:15,360 比如说我们写应用程序的时候 56 00:02:15,400 --> 00:02:16,800 如果你访问的是一个非法的地址 57 00:02:16,840 --> 00:02:18,160 那程序会crash 58 00:02:18,200 --> 00:02:21,440 或者被操作系统kill掉 那这是一个道理 59 00:02:21,480 --> 00:02:23,680 那如果程序访问的是一个合法的地址 60 00:02:23,720 --> 00:02:25,520 即使这个地址不在内存中 61 00:02:25,560 --> 00:02:27,560 那我们操作系统也能够有效应对 62 00:02:27,600 --> 00:02:29,960 这是我们页替换算法要去完成的工作 63 00:02:30,000 --> 00:02:32,240 所以我们会进一步对pgfault_handler 64 00:02:32,280 --> 00:02:37,360 做进一步的处理 会调do_pgfault 65 00:02:37,400 --> 00:02:39,600 那do_pgfault就开始判断了 66 00:02:39,640 --> 00:02:42,120 它需要找到你访问这个地址 67 00:02:42,160 --> 00:02:44,800 因为从这里面会除了说 68 00:02:44,840 --> 00:02:46,720 产生一个异常一个号之外 69 00:02:46,760 --> 00:02:48,960 还会给你一个说错在哪 70 00:02:49,000 --> 00:02:51,440 到底访问什么地址出现的错 71 00:02:51,480 --> 00:02:54,720 那这个find_vma假设是说看看 72 00:02:54,760 --> 00:02:57,480 这个地址是否是一个合法的地址 73 00:02:57,520 --> 00:02:59,440 上一页我们讲到我们首先要 74 00:02:59,480 --> 00:03:01,680 建立好一个vmm的一个环境 75 00:03:01,720 --> 00:03:04,160 这个环境由mm和vma组成 76 00:03:04,200 --> 00:03:08,400 它构造了一个使用者的一个虚存环境 77 00:03:08,440 --> 00:03:09,720 所以说呢在那个环境里面 78 00:03:09,760 --> 00:03:11,440 我们设定好了哪些页 79 00:03:11,480 --> 00:03:13,480 哪些地址空间是合法的 80 00:03:13,520 --> 00:03:14,840 哪些地址空间是不合法的 81 00:03:14,880 --> 00:03:16,000 所以它要去查找 82 00:03:16,040 --> 00:03:17,960 根据那个产生缺页异常之后 83 00:03:18,000 --> 00:03:21,600 反馈回来的这个异常地址来查找 84 00:03:21,640 --> 00:03:24,640 看这个地址是否属于某一个vma 85 00:03:24,680 --> 00:03:26,560 如果属于某一个vma的一个范围 86 00:03:26,600 --> 00:03:28,440 它有个开始地址 有个结束地址 87 00:03:28,480 --> 00:03:29,560 属于这个范围之内 88 00:03:29,600 --> 00:03:31,200 那这就是一个合法的地址 89 00:03:31,240 --> 00:03:32,440 既然是一个合法的地址 90 00:03:32,480 --> 00:03:33,720 那我们使用者就应该 91 00:03:33,760 --> 00:03:36,400 能够正确的去使用它 92 00:03:36,440 --> 00:03:37,720 而且还不用关注这种 93 00:03:37,760 --> 00:03:39,400 所谓缺页错误情况 94 00:03:39,440 --> 00:03:40,280 这些所有错误情况都是由我们说 95 00:03:40,320 --> 00:03:44,360 由我们虚存管理系统来完成 96 00:03:44,400 --> 00:03:46,920 帮它处理好 找到了这个 97 00:03:46,960 --> 00:03:50,880 是这个一个合法地址之后呢 98 00:03:50,920 --> 00:03:53,440 我们进一步要看看它是否是 99 00:03:53,480 --> 00:03:59,240 是否是一个位于硬盘中的一个页 100 00:03:59,280 --> 00:04:00,960 如果是位于硬盘中的一个页 101 00:04:01,000 --> 00:04:05,520 那我们要把这个页从我们硬盘中 102 00:04:05,560 --> 00:04:07,160 加载到内存中来 103 00:04:07,200 --> 00:04:08,400 OK 那这个过程是要走 104 00:04:08,440 --> 00:04:10,520 swap in / swap out这套机制 105 00:04:10,560 --> 00:04:11,800 我们前面已经讲了 106 00:04:11,840 --> 00:04:14,320 我们需要把这个硬盘的 107 00:04:14,360 --> 00:04:16,320 换入换出分区给建立好 108 00:04:16,360 --> 00:04:19,000 这个swap in会把这个地址 109 00:04:19,040 --> 00:04:21,880 把这个访问出现异常的地址 110 00:04:21,920 --> 00:04:23,920 转换成一个页号 111 00:04:23,960 --> 00:04:26,160 进一步把这个页号去映射 112 00:04:26,200 --> 00:04:28,040 对应到它所对应的 113 00:04:28,080 --> 00:04:29,400 那个磁盘扇区号里面 114 00:04:29,440 --> 00:04:30,440 把那个磁盘扇区里面 115 00:04:30,480 --> 00:04:32,480 存这个页的内容呢找着 116 00:04:32,520 --> 00:04:35,320 再通过这个对磁盘的read操作 117 00:04:35,360 --> 00:04:37,760 把所有数据读到内存中来 118 00:04:37,800 --> 00:04:40,360 读到内存中来 读到内存之后 119 00:04:40,400 --> 00:04:42,000 需要注意 读到内存之后 120 00:04:42,040 --> 00:04:43,920 我们重新还要建立好这个映射关系 121 00:04:43,960 --> 00:04:45,440 那我们读到内存来之后 122 00:04:45,480 --> 00:04:46,920 我们放到一个物理页里面 123 00:04:46,960 --> 00:04:50,600 我们需要把刚才产生异常虚拟地址的 124 00:04:50,640 --> 00:04:53,480 那个虚页和这个你刚才 125 00:04:53,520 --> 00:04:54,920 已经从我们这个硬盘中读进来 126 00:04:54,960 --> 00:04:56,240 这个物理页呢这两者之间 127 00:04:56,280 --> 00:04:58,520 建立一个对应关系 这个关系是什么 128 00:04:58,560 --> 00:05:00,920 就是我们页表的重新的建立 129 00:05:00,960 --> 00:05:04,000 这里面会分配相应的页表项 130 00:05:04,040 --> 00:05:06,160 甚至说可能会创建一个页表 131 00:05:06,200 --> 00:05:08,920 来完成对这个映射关系的一个建立 132 00:05:08,960 --> 00:05:10,840 这是它大致的一个处理过程 133 00:05:10,880 --> 00:05:12,480 一旦建立好这个映射关系 134 00:05:12,520 --> 00:05:14,000 那也意味着我们可以正确地 135 00:05:14,040 --> 00:05:18,480 让使用者去继续访问这一块内存了 136 00:05:18,520 --> 00:05:20,520 那也意味着 在这里 137 00:05:20,560 --> 00:05:21,600 处理完所有这些东西之后 138 00:05:21,640 --> 00:05:23,000 它要原路返回 139 00:05:23,040 --> 00:05:24,960 从这儿到_alltraps这儿 140 00:05:25,000 --> 00:05:26,520 再做一个iret 141 00:05:26,560 --> 00:05:29,040 把这个异常中断返回 142 00:05:29,080 --> 00:05:31,600 那这种异常它返回到哪去呢 143 00:05:31,640 --> 00:05:34,240 是返回到产生异常的 144 00:05:34,280 --> 00:05:36,280 那条指令重新执行 145 00:05:36,320 --> 00:05:38,320 正因为它能重新执行 146 00:05:38,360 --> 00:05:40,040 在第二次执行的时候需要注意 147 00:05:40,080 --> 00:05:41,280 第二次执行的时候 148 00:05:41,320 --> 00:05:43,680 由于你页表的映射关系 149 00:05:43,720 --> 00:05:45,080 已经重新建立好了 150 00:05:45,120 --> 00:05:45,960 所以说第二次执行的时候 151 00:05:46,000 --> 00:05:48,480 不会出现异常 当然前提是你做对 152 00:05:48,520 --> 00:05:50,440 如果做错了那还是会出现 153 00:05:50,480 --> 00:05:53,160 进一步的异常 需要进一步去处理 154 00:05:53,200 --> 00:05:54,240 假如说做对之后 155 00:05:54,280 --> 00:05:55,480 它可以继续往前走 156 00:05:55,520 --> 00:05:56,800 就把这个数据的读和写 157 00:05:56,840 --> 00:05:58,360 就可以正常的进行操作了 158 00:05:58,400 --> 00:06:01,160 这就是当产生页访问异常之后 159 00:06:01,200 --> 00:06:03,760 这个do_pgfault这个关键的函数 160 00:06:03,800 --> 00:06:05,400 它要去完成的一系列的工作 161 00:06:05,440 --> 00:06:06,520 当然这个函数的建立呢 162 00:06:06,560 --> 00:06:09,080 是需要我们刚才说到一系列的帮助 163 00:06:09,120 --> 00:06:10,240 需要我们的页表 164 00:06:10,280 --> 00:06:12,600 需要我们swap in / swap out这个disk读写 165 00:06:12,640 --> 00:06:14,960 需要我们vma环境的建立 166 00:06:15,000 --> 00:06:17,040 来一块协同起来 来帮助 167 00:06:17,080 --> 00:06:21,200 来完成这个do_pgfault这个工作 168 00:06:21,240 --> 00:06:23,360 另一方面我们可以看到就是 169 00:06:23,400 --> 00:06:25,760 页访问异常它里面我们说 170 00:06:25,800 --> 00:06:27,280 它怎么知道它错哪了呢 171 00:06:27,320 --> 00:06:28,880 它里面有一系列一些机制 172 00:06:28,920 --> 00:06:30,680 除了我们刚才说能够得到一个 173 00:06:30,720 --> 00:06:33,040 说到底访问哪个页出错之外呢 174 00:06:33,080 --> 00:06:36,440 其实它有一些不太合法的情况出现 175 00:06:36,480 --> 00:06:39,240 比如说我是一个用户态的一个程序 176 00:06:39,280 --> 00:06:40,760 我要访问一个内核态的地址 177 00:06:40,800 --> 00:06:43,480 那属于一个低优先级的一个程序 178 00:06:43,520 --> 00:06:45,720 要访问高优先级的一个内存就会出错 179 00:06:45,760 --> 00:06:47,560 这里面会有一些相应的位 180 00:06:47,600 --> 00:06:50,360 比如说U/S 就是user还是supervisor 181 00:06:50,400 --> 00:06:52,320 来区分这个页 182 00:06:52,360 --> 00:06:54,440 这个页到底是特权级的 183 00:06:54,480 --> 00:06:56,360 还是属于一般用户的 184 00:06:56,400 --> 00:06:58,520 W/R是区分是只读的 185 00:06:58,560 --> 00:07:00,400 还是可读写的等等 186 00:07:00,440 --> 00:07:01,760 这一系列的属性 187 00:07:01,800 --> 00:07:03,440 来映射了不同的关系 188 00:07:03,480 --> 00:07:05,000 我们这里面可以充分利用 189 00:07:05,040 --> 00:07:06,600 当然这里面我们用的比较简单 190 00:07:06,640 --> 00:07:07,760 只是说产生异常之后 191 00:07:07,800 --> 00:07:09,560 我们需要把这个页给映射回来 192 00:07:09,600 --> 00:07:11,800 所以说只要知道出错的地址在哪 193 00:07:11,840 --> 00:07:13,080 就OK了 194 00:07:13,120 --> 00:07:14,600 接下来后续的操作系统的讲解中 195 00:07:14,640 --> 00:07:17,360 也会用到这些相应的一些属性 196 00:07:17,400 --> 00:07:18,600 来完成特定的功能 197 00:07:18,640 --> 00:07:18,680