0 00:00:00,000 --> 00:00:15,920 1 00:00:15,960 --> 00:00:17,120 各位同学大家好 2 00:00:17,160 --> 00:00:20,440 今天我给大家介绍第四个实验 3 00:00:20,480 --> 00:00:25,120 就是关于内核线程的管理 4 00:00:25,160 --> 00:00:27,640 那我们这次课程呢 5 00:00:27,680 --> 00:00:29,480 主要包含以下几部分 6 00:00:29,520 --> 00:00:34,760 总体介绍 关键数据结构和执行流程 7 00:00:34,800 --> 00:00:36,400 首先我们看一下总体介绍 8 00:00:36,440 --> 00:00:37,520 总体介绍包含三个部分 9 00:00:37,560 --> 00:00:38,920 又包含三个小部分 10 00:00:38,960 --> 00:00:40,720 目标 练习和整个这个 11 00:00:40,760 --> 00:00:44,720 实验流程的一个概述 12 00:00:44,760 --> 00:00:45,760 那目标是什么呢 13 00:00:45,800 --> 00:00:47,720 我们前面已经完成了 14 00:00:47,760 --> 00:00:50,560 从lab1到lab3的一个实验 15 00:00:50,600 --> 00:00:52,200 那么在前面的实验中 16 00:00:52,240 --> 00:00:53,560 我们重点解决了什么呢 17 00:00:53,600 --> 00:00:55,440 重点解决是内存管理相关的 18 00:00:55,480 --> 00:00:57,640 一系列的一些关键的知识 19 00:00:57,680 --> 00:00:59,360 包括物理内存管理 20 00:00:59,400 --> 00:01:01,560 以及虚拟内存管理 21 00:01:01,600 --> 00:01:02,760 那在这个过程中呢 22 00:01:02,800 --> 00:01:04,000 我们已经掌握了 23 00:01:04,040 --> 00:01:06,040 就是关于内存的一部分的一些知识 24 00:01:06,080 --> 00:01:07,720 接下来呢我们要看一下 25 00:01:07,760 --> 00:01:09,200 对于我们CPU而言 26 00:01:09,240 --> 00:01:10,640 我们怎么能够充分利用CPU 27 00:01:10,680 --> 00:01:12,280 让CPU来执行多个 28 00:01:12,320 --> 00:01:14,840 不同的线程或者进程 29 00:01:14,880 --> 00:01:17,640 在这里面我们首先关注是线程 30 00:01:17,680 --> 00:01:19,080 因为相对来说在内核里面 31 00:01:19,120 --> 00:01:21,200 创建一个线程比在用户态 32 00:01:21,240 --> 00:01:23,360 创建进程要容易一些 33 00:01:23,400 --> 00:01:25,080 我们先从这个线程入手 34 00:01:25,120 --> 00:01:26,880 这也和我们大致原理课内容 35 00:01:26,920 --> 00:01:27,920 正好有点相反 36 00:01:27,960 --> 00:01:28,320 我们原理课一般是 37 00:01:28,360 --> 00:01:30,400 从进程开始讲起的 38 00:01:30,440 --> 00:01:31,160 但是没有关系 39 00:01:31,200 --> 00:01:32,160 因为它们很多部分 40 00:01:32,200 --> 00:01:37,400 都是有相同的地方 41 00:01:37,440 --> 00:01:38,440 大家再回忆一下 42 00:01:38,480 --> 00:01:40,240 原理课中讲过一些知识 43 00:01:40,280 --> 00:01:41,640 原理课我们讲一个线程 44 00:01:41,680 --> 00:01:43,920 或者进程在这里面我们不做区别 45 00:01:43,960 --> 00:01:45,800 那他有一个生命周期 46 00:01:45,840 --> 00:01:48,280 从最开始这个启动 47 00:01:48,320 --> 00:01:50,200 到最后的结束 48 00:01:50,240 --> 00:01:54,120 那么创建线程线程处于就绪态 49 00:01:54,160 --> 00:01:55,800 然后占用CPU执行 50 00:01:55,840 --> 00:01:57,200 然后由于某种事件的原因 51 00:01:57,240 --> 00:01:58,520 导致它的等待 52 00:01:58,560 --> 00:02:00,760 在这个整个过程最后还要退出 53 00:02:00,800 --> 00:02:01,600 那么这是 54 00:02:01,640 --> 00:02:04,320 我们说是一个线程一个生命周期 55 00:02:04,360 --> 00:02:07,640 那怎么去有效管理生命周期呢 56 00:02:07,680 --> 00:02:09,400 这就需要在我们操作系统里面 57 00:02:09,440 --> 00:02:11,200 有一个专门数据结构 58 00:02:11,240 --> 00:02:14,120 我们称之为TCB 59 00:02:14,160 --> 00:02:15,200 线程控制块 60 00:02:15,240 --> 00:02:17,400 当然我们可以叫PCB 61 00:02:17,440 --> 00:02:19,280 就是进程控制块 62 00:02:19,320 --> 00:02:21,160 在这里面我们一样不做区分 63 00:02:21,200 --> 00:02:22,200 在本次实验中呢 64 00:02:22,240 --> 00:02:23,440 我们重点讲的是线程 65 00:02:23,480 --> 00:02:24,560 所以我们可以在以后 66 00:02:24,600 --> 00:02:30,480 都用TCB来进行一个表述 67 00:02:30,520 --> 00:02:32,840 那第二方面呢我们希望去了解 68 00:02:32,880 --> 00:02:34,600 这个线程执行一个过程 69 00:02:34,640 --> 00:02:35,280 怎么去执行它 70 00:02:35,320 --> 00:02:36,480 怎么去调度它 71 00:02:36,520 --> 00:02:37,880 那我们可以看到 72 00:02:37,920 --> 00:02:39,760 对于一个线程而言它需要管理的 73 00:02:39,800 --> 00:02:42,720 除了它的代码段 数据段之外呢 74 00:02:42,760 --> 00:02:44,720 还有一个执行的流程 75 00:02:44,760 --> 00:02:46,080 我们需要知道它在 76 00:02:46,120 --> 00:02:48,080 某一个时刻执行到什么地方 77 00:02:48,120 --> 00:02:50,440 这也就是说对于这个线程执行过程呢 78 00:02:50,480 --> 00:02:53,320 我们希望有效管理措施 79 00:02:53,360 --> 00:02:57,720 可以使得单线程和多线程 80 00:02:57,760 --> 00:02:58,920 都能够在我们系统里面 81 00:02:58,960 --> 00:03:00,320 能够正常的工作 82 00:03:00,360 --> 00:03:01,360 对于单线程而言 83 00:03:01,400 --> 00:03:02,640 我们可能涉及不到调度 84 00:03:02,680 --> 00:03:03,720 但是对于多线程而言 85 00:03:03,760 --> 00:03:06,440 我们就会碰到到底在什么时候 86 00:03:06,480 --> 00:03:09,080 应该让这个线程去占用CPU执行 87 00:03:09,120 --> 00:03:09,920 因为存在多个线程 88 00:03:09,960 --> 00:03:11,720 抢一个CPU的情况 89 00:03:11,760 --> 00:03:12,840 所以说呢 90 00:03:12,880 --> 00:03:15,840 在这里面我们希望让大家能够了解到 91 00:03:15,880 --> 00:03:18,600 大致一个线程怎么去创建 92 00:03:18,640 --> 00:03:20,120 以及他怎么去执行 93 00:03:20,160 --> 00:03:21,800 甚至说还进一步去考虑 94 00:03:21,840 --> 00:03:24,240 两个线程或者多线程怎么去切换 95 00:03:24,280 --> 00:03:28,280 来分时的占用CPU来执行的一个过程 96 00:03:28,320 --> 00:03:33,680 这是说我们这个lab4重点要考虑的内容 97 00:03:33,720 --> 00:03:35,760 为此呢你可以看到我们需要 98 00:03:35,800 --> 00:03:40,320 去对TCB做一个比较全面的理解和分析 99 00:03:40,360 --> 00:03:42,800 那么充分利用TCB里面的一些信息 100 00:03:42,840 --> 00:03:48,120 来完成对这个线程的有效的管理 101 00:03:48,160 --> 00:03:49,680 好 那这时候我们讲完这个 102 00:03:49,720 --> 00:03:52,120 关于这个线程目标这一块 103 00:03:52,160 --> 00:03:52,920 我们接下来看一看 104 00:03:52,960 --> 00:03:55,160 我们这个实验要完成的练习 105 00:03:55,200 --> 00:03:56,880 练习有三个 106 00:03:56,920 --> 00:03:59,240 第一个就是你要知道怎么去初始化 107 00:03:59,280 --> 00:04:00,760 创建一个进程控制块 108 00:04:00,800 --> 00:04:01,600 或者线程控制块 109 00:04:01,640 --> 00:04:02,720 这个不做区分 110 00:04:02,760 --> 00:04:05,600 第二个呢 我们会讲怎么为这个 111 00:04:05,640 --> 00:04:08,000 内核的线程分配资源 112 00:04:08,040 --> 00:04:09,240 有了控制块之后呢 113 00:04:09,280 --> 00:04:10,960 它是用来管理 114 00:04:11,000 --> 00:04:12,360 管理很多元数据 115 00:04:12,400 --> 00:04:13,920 管理这个比如说这个线程 116 00:04:13,960 --> 00:04:16,280 占用空间占用堆栈等等 117 00:04:16,320 --> 00:04:18,000 那这些信息怎么能够 118 00:04:18,040 --> 00:04:20,240 怎么能够在创建线程的时候 119 00:04:20,280 --> 00:04:21,280 给它分配好相应 120 00:04:21,320 --> 00:04:25,480 他所需要能够正常运行需要这个资源 121 00:04:25,520 --> 00:04:28,080 第三个呢要理解一个就是相对来说 122 00:04:28,120 --> 00:04:30,400 和我们硬件相关的一个细节 123 00:04:30,440 --> 00:04:31,920 就是怎么去切换的 124 00:04:31,960 --> 00:04:33,400 当有两个进程存在的时候 125 00:04:33,440 --> 00:04:34,280 那我们怎么去能够 126 00:04:34,320 --> 00:04:36,120 完成进程和线程的切换 127 00:04:36,160 --> 00:04:37,680 这是我们说希望 128 00:04:37,720 --> 00:04:39,640 大家通过练习能够掌握的 129 00:04:39,680 --> 00:04:41,200 当然我们在后续的讲解中 130 00:04:41,240 --> 00:04:43,800 对这里面一些关键点和难点呢 131 00:04:43,840 --> 00:04:44,880 给大家做一个介绍 132 00:04:44,920 --> 00:04:46,040 以便于大家能够 133 00:04:46,080 --> 00:04:49,960 比较方便的开展相应的练习 134 00:04:50,000 --> 00:04:52,000 那如果说你完成了这个练习 135 00:04:52,040 --> 00:04:54,440 那么可以看到我们最终会创建出 136 00:04:54,480 --> 00:04:56,880 创建出一个内核线程 137 00:04:56,920 --> 00:04:59,040 它可以完成一个最简单的 138 00:04:59,080 --> 00:05:00,760 hello world一个显示 139 00:05:00,800 --> 00:05:01,880 别小看这个hello world 140 00:05:01,920 --> 00:05:03,800 虽然感觉只是打一个字符串 141 00:05:03,840 --> 00:05:07,040 但其实我们为此做出了很多的工作 142 00:05:07,080 --> 00:05:09,680 包括从lab1建立中断机制 143 00:05:09,720 --> 00:05:10,960 跟外设打交道 144 00:05:11,000 --> 00:05:12,400 lab2管理物理内存 145 00:05:12,440 --> 00:05:13,960 lab3管理虚拟内存 146 00:05:14,000 --> 00:05:15,400 有了前三步基础之后呢 147 00:05:15,440 --> 00:05:22,400 我们才能够去创建一个进程或者线程 148 00:05:22,440 --> 00:05:23,400 那么接下来我们看一下 149 00:05:23,440 --> 00:05:26,400 整个一个实验运行一个流程 150 00:05:26,440 --> 00:05:28,040 首先我们需要去能够 151 00:05:28,080 --> 00:05:30,160 对他的关键数据结构进行一个处理 152 00:05:30,200 --> 00:05:31,400 关键数据结构包含两部分 153 00:05:31,440 --> 00:05:32,880 一个是说我们要有TCB 154 00:05:32,920 --> 00:05:34,640 就是线程控制块 155 00:05:34,680 --> 00:05:37,040 第二个呢 当存在多个线程的时候呢 156 00:05:37,080 --> 00:05:38,240 我们需要把这些线程 157 00:05:38,280 --> 00:05:39,800 有效的管理起来 158 00:05:39,840 --> 00:05:40,720 一堆线程管理起来 159 00:05:40,760 --> 00:05:42,160 那么需要有一个list 160 00:05:42,200 --> 00:05:44,320 这个线程控制块一个列表 161 00:05:44,360 --> 00:05:46,320 那我们可以用list结构呢 162 00:05:46,360 --> 00:05:47,760 来对它进行有效的管理 163 00:05:47,800 --> 00:05:49,080 有了这两部分之后呢 164 00:05:49,120 --> 00:05:51,080 我们就可以说我们一个对线程 165 00:05:51,120 --> 00:05:54,800 关键数据结构呢就可以建好了 166 00:05:54,840 --> 00:05:57,240 第二部分呢就是要去怎么去操作了 167 00:05:57,280 --> 00:05:58,200 那么对于操作而言呢 168 00:05:58,240 --> 00:06:00,440 我们首先要完成相应的初始化 169 00:06:00,480 --> 00:06:03,760 在前面的lab1到lab3 170 00:06:03,800 --> 00:06:06,320 完成了包括中断物理内存 171 00:06:06,360 --> 00:06:08,040 和虚拟内存的初始化工作 172 00:06:08,080 --> 00:06:09,640 这时候我们打下一个基础 173 00:06:09,680 --> 00:06:11,000 那在这个实验中我们还要 174 00:06:11,040 --> 00:06:12,560 进一步要去初始化什么呢 175 00:06:12,600 --> 00:06:14,800 我们内核线程本身初始化 176 00:06:14,840 --> 00:06:17,200 还有怎么去创建相应的 177 00:06:17,240 --> 00:06:18,960 一些关键的一些资源 178 00:06:19,000 --> 00:06:21,800 就是为这个线程要去分配一些资源 179 00:06:21,840 --> 00:06:25,440 然后还要能够去实现线程的切换 180 00:06:25,480 --> 00:06:26,800 只要把这几部分 181 00:06:26,840 --> 00:06:28,840 初始化工作做完之后呢 182 00:06:28,880 --> 00:06:31,920 你才能够有效去让一个线程 183 00:06:31,960 --> 00:06:34,920 能够正确切换和运行 184 00:06:34,960 --> 00:06:36,360 好 最后一步呢就是说 185 00:06:36,400 --> 00:06:38,800 我们能够让我们前面创建好 186 00:06:38,840 --> 00:06:40,480 这个线程能够切换去 187 00:06:40,520 --> 00:06:41,560 让它去执行 188 00:06:41,600 --> 00:06:42,480 从而可以打印出我们 189 00:06:42,520 --> 00:06:45,120 刚才说到这个字符串的信息 190 00:06:45,160 --> 00:06:46,520 可见为了完成这个工作 191 00:06:46,560 --> 00:06:48,040 确实要完成大量前期 192 00:06:48,080 --> 00:06:49,320 一些初始化的工作 193 00:06:49,360 --> 00:06:50,240 当然我们在后面会 194 00:06:50,280 --> 00:06:52,840 逐一给大家讲解其中的一些关键点 195 00:06:52,880 --> 00:06:53,000 196 00:06:53,040 --> 00:06:53,080