0 00:00:00,000 --> 00:00:06,720 1 00:00:06,840 --> 00:00:08,280 好 我们先来看第一部分的内容 2 00:00:08,320 --> 00:00:10,280 就是关于这个关键的数据结构 3 00:00:10,320 --> 00:00:12,400 这里面前面已经提到有线程控制块 4 00:00:12,440 --> 00:00:16,200 和线程控制块的一个列表 5 00:00:16,240 --> 00:00:17,680 那我们前面讲到这个线程控制块 6 00:00:17,720 --> 00:00:19,080 这个名词很简单 7 00:00:19,120 --> 00:00:21,360 thread control block 8 00:00:21,440 --> 00:00:24,280 感觉就是一个很典型数据结构 9 00:00:24,320 --> 00:00:25,600 但这个数据结构里面 10 00:00:25,800 --> 00:00:31,520 包含着什么信息呢 11 00:00:31,560 --> 00:00:33,360 可以看到有很多的 12 00:00:33,400 --> 00:00:35,680 它的一些成员变量在里面 13 00:00:35,720 --> 00:00:37,160 那这成员变量里面虽然多 14 00:00:37,200 --> 00:00:38,480 但没关系大家可以逐一 15 00:00:38,520 --> 00:00:40,240 把它进行区分出来 16 00:00:40,280 --> 00:00:43,640 首先看看 第一个它的ID 17 00:00:43,680 --> 00:00:45,120 我们说作为一个线程来说 18 00:00:45,160 --> 00:00:46,120 它有它自己的ID 19 00:00:46,160 --> 00:00:47,280 所以我们有一个PID 20 00:00:47,320 --> 00:00:49,000 来代表它的一个身份 21 00:00:49,040 --> 00:00:50,600 就跟我们身份证是一样的 22 00:00:50,640 --> 00:00:51,880 唯一的一个ID 23 00:00:51,920 --> 00:00:53,880 同时呢它就要有一个名字 24 00:00:53,920 --> 00:00:54,680 你有身份证之外 25 00:00:54,720 --> 00:00:55,960 你自己还有一个名字也是一样 26 00:00:56,000 --> 00:00:57,480 更好的表述这个线程 27 00:00:57,520 --> 00:00:59,040 那么这一块是跟它的一个 28 00:00:59,080 --> 00:01:01,840 身份标识相关的一部分信息 29 00:01:01,880 --> 00:01:05,120 这是第一部分信息 30 00:01:05,160 --> 00:01:06,760 那第二部分信息是什么呢 31 00:01:06,800 --> 00:01:09,040 第二部分信息是关于它的一个 32 00:01:09,080 --> 00:01:10,960 运行时候跟调度相关的 33 00:01:11,000 --> 00:01:12,640 一些管理控制一些信息 34 00:01:12,680 --> 00:01:14,400 比如说它当天是属于什么状态 35 00:01:14,440 --> 00:01:18,320 是属于就绪态 运行态还是等待状态 36 00:01:18,360 --> 00:01:19,800 那这里面有一个state来表明 37 00:01:19,840 --> 00:01:22,360 当前运行状态是动态变化的信息 38 00:01:22,400 --> 00:01:23,000 需要注意 39 00:01:23,040 --> 00:01:24,680 随着这个线程的运行呢 40 00:01:24,720 --> 00:01:26,560 它这个状态会发生变化 41 00:01:26,600 --> 00:01:29,200 第二个呢 它是否需要调度 42 00:01:29,240 --> 00:01:30,840 以及在某些细节上面 43 00:01:30,880 --> 00:01:32,480 有runs flags等信息 44 00:01:32,520 --> 00:01:34,680 都是跟我们说跟他动态运行相关 45 00:01:34,720 --> 00:01:37,080 一些信息在这里面有表述 46 00:01:37,120 --> 00:01:39,120 OK 这是第二部分 47 00:01:39,160 --> 00:01:40,800 动态运行相关的一部分信息 48 00:01:40,840 --> 00:01:42,480 第三部分呢 49 00:01:42,520 --> 00:01:44,240 第三部分是跟内存管理相关 50 00:01:44,280 --> 00:01:45,240 就是我们说 51 00:01:45,280 --> 00:01:47,120 线程也要占一定的空间 52 00:01:47,160 --> 00:01:50,480 那么它到底需要哪些内存资源 53 00:01:50,520 --> 00:01:52,200 这一点在这里面有个表述 54 00:01:52,240 --> 00:01:55,520 可以看到这里面有一个kstack 55 00:01:55,560 --> 00:01:57,400 就是内核里面的一个堆栈 56 00:01:57,440 --> 00:02:00,160 这里面大家需要注意 57 00:02:00,200 --> 00:02:01,160 因为我们这是一个内核线程 58 00:02:01,200 --> 00:02:02,800 所以它有个内核的堆栈 59 00:02:02,840 --> 00:02:04,080 那我们后续讲lab5的时候 60 00:02:04,120 --> 00:02:05,200 会讲到用户进程 61 00:02:05,240 --> 00:02:07,800 那么用户进程其实也有个内核堆栈 62 00:02:07,840 --> 00:02:10,400 那我们后面会进一步做阐述 63 00:02:10,440 --> 00:02:11,640 第二个CR3 64 00:02:11,680 --> 00:02:13,320 CR3在这里面没太用上 65 00:02:13,360 --> 00:02:14,480 因为我们在这里面呢 66 00:02:14,520 --> 00:02:16,080 其实既然是线程 67 00:02:16,120 --> 00:02:17,480 我们讲过线程的原理 68 00:02:17,520 --> 00:02:20,440 它会用所谓进程的一个页表来共享 69 00:02:20,480 --> 00:02:21,800 共享这个地址空间 70 00:02:21,840 --> 00:02:23,240 对于我们内核线程而言 71 00:02:23,280 --> 00:02:25,360 它对应那个进程是什么呢 72 00:02:25,400 --> 00:02:26,360 其实大家可以想象到 73 00:02:26,400 --> 00:02:27,680 其实就是我们ucore操作系统 74 00:02:27,720 --> 00:02:29,760 我们ucore操作系统在开始的时候 75 00:02:29,800 --> 00:02:32,320 比如说在我们lab1和lab2 76 00:02:32,360 --> 00:02:33,360 会完成一个什么呢 77 00:02:33,400 --> 00:02:35,120 会完成一个页表的建立过程 78 00:02:35,160 --> 00:02:36,200 那那个建立的页表呢 79 00:02:36,240 --> 00:02:38,200 就会被我们线程所使用 80 00:02:38,240 --> 00:02:39,960 所以它共用了我们的ucore 81 00:02:40,000 --> 00:02:41,600 OS一个页表 82 00:02:41,640 --> 00:02:44,280 第三个mm这个结构 83 00:02:44,320 --> 00:02:45,800 那么这个结构也是一样的 84 00:02:45,840 --> 00:02:47,080 其实这个结构呢 85 00:02:47,120 --> 00:02:49,520 我们还会进一步展开 86 00:02:49,560 --> 00:02:53,000 这是用来管理线程或者进程 87 00:02:53,040 --> 00:02:54,960 它所需要的这个内存空间的 88 00:02:55,000 --> 00:02:57,040 当然我们前面讲到对于线程而言 89 00:02:57,080 --> 00:02:59,680 这一块其实不用太多区分 90 00:02:59,720 --> 00:03:01,000 但是讲到进程时候 91 00:03:01,040 --> 00:03:03,920 我们会知道进程有它合法内存空间 92 00:03:03,960 --> 00:03:06,800 所以说在mm里面管理了什么 93 00:03:06,840 --> 00:03:09,280 mm管理了一个进程 94 00:03:09,320 --> 00:03:11,320 它所需要合法内存空间 95 00:03:11,360 --> 00:03:13,360 这个每一个合法内存空间内存块 96 00:03:13,400 --> 00:03:16,040 我们用一个vma来表示 97 00:03:16,080 --> 00:03:18,760 所以说呢有一系列的内存块 98 00:03:18,800 --> 00:03:20,760 合法的内存块用vma表示内存块 99 00:03:20,800 --> 00:03:24,440 来构成了整个这个进程 100 00:03:24,480 --> 00:03:26,440 或者线程内存空间 101 00:03:26,480 --> 00:03:29,240 但是对于我们内核线程而言 102 00:03:29,280 --> 00:03:30,480 这一块信息也是不需要的 103 00:03:30,520 --> 00:03:34,200 因为它都在我们统一的ucore管理之下 104 00:03:34,240 --> 00:03:36,880 所有的内存空间它都可以看得到 105 00:03:36,920 --> 00:03:40,040 所以说在这里面可以看到这些vma 106 00:03:40,080 --> 00:03:42,240 都属于同一个进程的 107 00:03:42,280 --> 00:03:44,800 mm这个结构来进行管理 108 00:03:44,840 --> 00:03:46,280 还有呢就是它这个list 109 00:03:46,320 --> 00:03:46,880 它有一个list 110 00:03:46,920 --> 00:03:49,080 就所有的vma本身形成一个list 111 00:03:49,120 --> 00:03:50,400 它的头会放在这儿 112 00:03:50,440 --> 00:03:52,760 叫做mmap_list这是形成一个 113 00:03:52,800 --> 00:03:57,040 就是对内存空间的一个管理 114 00:03:57,080 --> 00:04:01,960 对于lab4而言这一块可以忽略掉 115 00:04:02,000 --> 00:04:04,280 好 我们再看看下一块 116 00:04:04,320 --> 00:04:05,960 这一块呢 相对来说复杂一点 117 00:04:06,000 --> 00:04:08,760 而且跟我们硬件是紧密相关的 118 00:04:08,800 --> 00:04:12,400 一个叫context一个叫trapframe 119 00:04:12,440 --> 00:04:13,920 那么context就我们通常说的 120 00:04:13,960 --> 00:04:17,080 所谓的进程或者线程的上下文 121 00:04:17,120 --> 00:04:17,760 什么叫上下文 122 00:04:17,800 --> 00:04:19,840 就在于我们说一个进程在运行的时候 123 00:04:19,880 --> 00:04:22,560 它当前说处的状态就是一个上下文 124 00:04:22,600 --> 00:04:25,080 这个听起来感觉好像是比较抽象 125 00:04:25,120 --> 00:04:28,080 我们会给大家展开看看什么是上下文 126 00:04:28,120 --> 00:04:29,200 对于我们ucore操作系统而言 127 00:04:29,240 --> 00:04:31,800 它运行在80386这个环境之下 128 00:04:31,840 --> 00:04:33,800 它所谓上下文是什么呢 129 00:04:33,840 --> 00:04:35,360 看就这些东西 130 00:04:35,400 --> 00:04:36,880 看起来大家都很熟悉 131 00:04:36,920 --> 00:04:39,920 其实就是一堆寄存器 132 00:04:39,960 --> 00:04:44,480 从eip、esp一直到通用的ebx、ecx等等 133 00:04:44,520 --> 00:04:46,720 那么这构成了一个上下文 134 00:04:46,760 --> 00:04:48,080 那我们说要切换上下文 135 00:04:48,120 --> 00:04:50,840 就是切换这些寄存器内容 136 00:04:50,880 --> 00:04:53,520 这是很重要就是说代表了 137 00:04:53,560 --> 00:04:56,240 当前的这个线程 138 00:04:56,280 --> 00:04:59,440 或者进程它运行的一个状态 139 00:04:59,480 --> 00:05:00,400 这个state 140 00:05:00,440 --> 00:05:01,920 需要注意这个state更detail 141 00:05:01,960 --> 00:05:03,400 更详细一个state和刚才说到 142 00:05:03,440 --> 00:05:06,840 那个运行的属于ready态还是就绪态 143 00:05:06,880 --> 00:05:09,640 那个state相比呢那更抽象更高层次 144 00:05:09,680 --> 00:05:11,400 这个是很具体 145 00:05:11,440 --> 00:05:12,520 比如运行到什么地方 146 00:05:12,560 --> 00:05:15,240 放在什么地方eip这个地方来保存着 147 00:05:15,280 --> 00:05:16,840 它会把这个信息保存下来 148 00:05:16,880 --> 00:05:19,720 这是说的context 149 00:05:19,760 --> 00:05:23,880 第二个呢 我们称之为 trapframe 150 00:05:23,920 --> 00:05:26,280 trapframe大家想一想 151 00:05:26,320 --> 00:05:28,200 好像在哪好像依稀见过 152 00:05:28,240 --> 00:05:31,720 如果大家对我们的实验还有印象的话 153 00:05:31,760 --> 00:05:33,040 大家想想我们在lab1的时候 154 00:05:33,080 --> 00:05:34,400 已经碰到了中断 155 00:05:34,440 --> 00:05:36,680 那其实中断里面就用到了trapframe 156 00:05:36,720 --> 00:05:38,080 只是我们对当时trapframe 157 00:05:38,120 --> 00:05:39,680 没有做深入的分析 158 00:05:39,720 --> 00:05:41,400 到了这里面呢我们会对trapframe 159 00:05:41,440 --> 00:05:43,680 做一个比较详细一个解释 160 00:05:43,720 --> 00:05:45,280 因为它已经涉及到了 161 00:05:45,320 --> 00:05:47,080 我们怎么去能够让这个线程 162 00:05:47,120 --> 00:05:49,440 或者进程能够正确的运行 163 00:05:49,480 --> 00:05:51,760 我们需要对它进行一些设置 164 00:05:51,800 --> 00:05:57,080 在创建进程的时候对它做相应的设置 165 00:05:57,120 --> 00:05:58,400 那trapframe也是一个 166 00:05:58,440 --> 00:05:59,360 比较复杂的数据结构 167 00:05:59,400 --> 00:06:01,160 我们分几个部分来讲解 168 00:06:01,200 --> 00:06:05,160 第一部分你可以看找eip cs 169 00:06:05,200 --> 00:06:07,120 还有e_flags 170 00:06:07,160 --> 00:06:10,160 还有一个err erro code 171 00:06:10,200 --> 00:06:12,040 这几块呢就是说 172 00:06:12,080 --> 00:06:14,160 我们前面解释过在中断产生的时候 173 00:06:14,200 --> 00:06:16,320 或者中断或者异常产生的时候呢 174 00:06:16,360 --> 00:06:20,200 我们的硬件会把这些信息 175 00:06:20,240 --> 00:06:22,280 放到内核堆栈里面去 176 00:06:22,320 --> 00:06:24,840 OK 所以说这里面其实是 177 00:06:24,880 --> 00:06:26,520 放在内核堆栈里面一部分 178 00:06:26,560 --> 00:06:28,640 跟trap frame相关的一些信息 179 00:06:28,680 --> 00:06:34,080 保存了当前被打断时候一些信息 180 00:06:34,120 --> 00:06:35,120 第二部分是什么呢 181 00:06:35,160 --> 00:06:38,920 第二部分是esp和ss为什么有这个信息 182 00:06:38,960 --> 00:06:41,360 这个信息其实也是和我们硬件相关的 183 00:06:41,400 --> 00:06:44,960 也是硬件来负责放到我们堆栈里面的 184 00:06:45,000 --> 00:06:47,080 但是什么情况下有这个呢 185 00:06:47,120 --> 00:06:49,680 什么情况没这个呢大家回忆一下 186 00:06:49,720 --> 00:06:51,840 我们讲lab1的时候也提到过 187 00:06:51,880 --> 00:06:53,880 如果要实现特权级的切换 188 00:06:53,920 --> 00:06:56,200 比如从特权级3切到 189 00:06:56,240 --> 00:06:58,200 特权级0那么这时候呢 190 00:06:58,240 --> 00:07:01,000 它会把这个esp和ss 191 00:07:01,040 --> 00:07:03,160 也会压入到这个栈里面去 192 00:07:03,200 --> 00:07:05,640 所以说这一块记录的是 193 00:07:05,680 --> 00:07:07,600 当发生特权级变换之后 194 00:07:07,640 --> 00:07:10,520 我们硬件要去压栈的一些信息 195 00:07:10,560 --> 00:07:12,160 这样会确保将来能够 196 00:07:12,200 --> 00:07:14,080 顺利回到ring3里面去 197 00:07:14,120 --> 00:07:15,520 从ring0回到ring3 198 00:07:15,560 --> 00:07:18,080 那么这时候实际上为了我们说lab5 199 00:07:18,120 --> 00:07:25,040 实现用户级的进程做好一个准备工作 200 00:07:25,080 --> 00:07:26,640 还有一块信息 201 00:07:26,680 --> 00:07:28,000 这块信息比较多 202 00:07:28,040 --> 00:07:29,400 可以看到这里面我们说 203 00:07:29,440 --> 00:07:31,840 段寄存器的信息 204 00:07:31,880 --> 00:07:34,440 以及这里面是通用寄存器的信息 205 00:07:34,480 --> 00:07:36,840 这个信息和我们刚才讲的context有点类似 206 00:07:36,880 --> 00:07:39,040 没错 这是寄存器的信息 207 00:07:39,080 --> 00:07:40,640 这是段寄存器的信息 208 00:07:40,680 --> 00:07:42,240 那么这两个信息呢 209 00:07:42,280 --> 00:07:44,080 是我们软件来保存的 210 00:07:44,120 --> 00:07:46,640 因为当前你打断了 211 00:07:46,680 --> 00:07:47,960 某一个执行流程之后 212 00:07:48,000 --> 00:07:50,240 我们硬件会保存这两部分信息 213 00:07:50,280 --> 00:07:51,440 由于你后续的执行 214 00:07:51,480 --> 00:07:52,360 比如说我们在处理 215 00:07:52,400 --> 00:07:53,440 中断服务例程的时候 216 00:07:53,480 --> 00:07:56,080 你还会破坏相应的段寄存器 217 00:07:56,120 --> 00:07:57,840 或者通用寄存器 218 00:07:57,880 --> 00:07:58,800 所以把它这些 219 00:07:58,840 --> 00:08:00,280 所有寄存器都给保存起来 220 00:08:00,320 --> 00:08:01,880 以便于后续能够恢复 221 00:08:01,920 --> 00:08:03,320 那trapframe是什么呢 222 00:08:03,360 --> 00:08:06,120 trapframe其实就是保存了前一个 223 00:08:06,160 --> 00:08:10,800 被打断的进程或者线程它当前一个状态 224 00:08:10,840 --> 00:08:11,640 被打断需要注意 225 00:08:11,680 --> 00:08:12,840 这里面被打断被谁打断 226 00:08:12,880 --> 00:08:15,000 被我们中断(包括trap)或者异常打断 227 00:08:15,040 --> 00:08:20,800 这就是trapframe一些信息 228 00:08:20,840 --> 00:08:23,000 好 我们再回到我们线程控制块 229 00:08:23,040 --> 00:08:24,360 可以看到我们最后一部分 230 00:08:24,400 --> 00:08:26,840 最后一部分是一些list 231 00:08:26,880 --> 00:08:28,320 这个list记录什么信息呢 232 00:08:28,360 --> 00:08:30,480 一个是记录它的父进程的信息 233 00:08:30,520 --> 00:08:32,720 就是谁创建了线程 234 00:08:32,760 --> 00:08:34,560 在这里面有一个parents 235 00:08:34,600 --> 00:08:36,280 第二个呢是两个link 236 00:08:36,320 --> 00:08:37,440 这两个link实际上构成了 237 00:08:37,480 --> 00:08:43,160 我们所谓的线程控制块的链表 238 00:08:43,200 --> 00:08:45,960 这是一个双向链表这是一个link 239 00:08:46,000 --> 00:08:47,160 如果你有N个线程的话 240 00:08:47,200 --> 00:08:49,760 它们按照这种顺序可以链起来 241 00:08:49,800 --> 00:08:51,800 当然我发现如果是采取这种方式的话 242 00:08:51,840 --> 00:08:54,120 当你线程比较多的情况下 243 00:08:54,160 --> 00:08:55,960 那你查找某个线程的时候 244 00:08:56,000 --> 00:08:58,000 所花的开销会比较大 245 00:08:58,040 --> 00:08:59,240 为此呢我们还专门建一个 246 00:08:59,280 --> 00:09:01,200 基于哈希值一个list 247 00:09:01,240 --> 00:09:04,000 那我们相对来说它查找对应的线程 248 00:09:04,040 --> 00:09:04,680 对应什么查找呢 249 00:09:04,720 --> 00:09:07,400 对应pid的查找它会比较快一些 250 00:09:07,440 --> 00:09:07,760 251 00:09:07,800 --> 00:09:07,840