0 00:00:00,000 --> 00:00:06,800 1 00:00:06,840 --> 00:00:07,920 那我们现在结合一下 2 00:00:07,960 --> 00:00:09,520 就是实际操作来看一下 3 00:00:09,560 --> 00:00:12,200 实验4所需要关注的一些细节 4 00:00:12,240 --> 00:00:14,120 那实验4内核线程管理 5 00:00:14,160 --> 00:00:16,120 要完成的练习呢不多 6 00:00:16,160 --> 00:00:16,920 主要是能够知道 7 00:00:16,960 --> 00:00:18,640 怎么去分配一个进程控制块 8 00:00:18,680 --> 00:00:20,400 以及怎么能够去 9 00:00:20,440 --> 00:00:22,520 调度 执行一个内核线程 10 00:00:22,560 --> 00:00:25,440 那在这里面呢有三个练习 11 00:00:25,480 --> 00:00:27,040 练习1 练习2 练习3 12 00:00:27,080 --> 00:00:28,440 其实应对的呢 13 00:00:28,480 --> 00:00:30,800 应对的就是主要是在哪呢 14 00:00:30,840 --> 00:00:33,440 在这个代码里面的这个地方 15 00:00:33,480 --> 00:00:36,240 就是proc.c 16 00:00:36,280 --> 00:00:39,320 proc.c里面呢有两个函数需要关注 17 00:00:39,360 --> 00:00:43,480 一个是叫做proc_init这么一个函数 18 00:00:43,520 --> 00:00:45,320 它会设置idle_proc 19 00:00:45,360 --> 00:00:47,960 并进一步去创建init_proc 20 00:00:48,000 --> 00:00:50,720 就第0号线程和第1号线程 21 00:00:50,760 --> 00:00:54,440 然后呢第二个是do_fork 22 00:00:54,480 --> 00:00:56,720 这个函数会完成具体的 23 00:00:56,760 --> 00:00:58,360 特别是针对init_proc 24 00:00:58,400 --> 00:01:01,200 一个它的内部线程控制块 25 00:01:01,240 --> 00:01:02,640 一个初始化工作 26 00:01:02,680 --> 00:01:05,720 然后呢当把这些初始化完毕之后 27 00:01:05,760 --> 00:01:08,200 我们可以看到在我们的总控程序 28 00:01:08,240 --> 00:01:10,320 就是ucore里面总控函数 29 00:01:10,360 --> 00:01:11,840 code_init里面呢 30 00:01:11,880 --> 00:01:15,320 会去进一步去调这个cpu_idle 31 00:01:15,360 --> 00:01:17,880 cpu_idle就是去查找 32 00:01:17,920 --> 00:01:20,840 当前这个idle_proc 33 00:01:20,880 --> 00:01:22,520 它是不是need reschedule 34 00:01:22,560 --> 00:01:24,400 其实我们前面已经做过介绍 35 00:01:24,440 --> 00:01:26,840 初始化是need reschedule是等于1的 36 00:01:26,880 --> 00:01:28,720 如果等于1表明它可以被切换掉 37 00:01:28,760 --> 00:01:30,280 换下一个线程去执行 38 00:01:30,320 --> 00:01:31,760 那么会调schedule 39 39 00:01:31,800 --> 00:01:33,880 schedule也就是说会把 40 00:01:33,920 --> 00:01:36,600 我们刚才说的那个线程那个队列 41 00:01:36,640 --> 00:01:37,640 线程控制块的队列呢 42 00:01:37,680 --> 00:01:39,520 找出对应的一个 43 00:01:39,560 --> 00:01:42,240 处于ready态一个线程去执行它 44 00:01:42,280 --> 00:01:44,200 当然这里面我们细节不用看(lab6会详细分析) 45 00:01:44,240 --> 00:01:45,920 大致可以看着在这里面函数proc_run 46 00:01:45,960 --> 00:01:50,520 proc_run它可以调switch_to 47 00:01:50,560 --> 00:01:51,880 我们后面讲到过的 48 00:01:51,920 --> 00:01:55,600 关于怎么去把它的内核的栈 49 00:01:55,640 --> 00:02:00,960 它的页表相关的页目录表的基址寄存器 50 00:02:01,000 --> 00:02:03,240 以及它的context做一个切换 51 00:02:03,280 --> 00:02:05,160 做完这个之后在switch_to 52 00:02:05,200 --> 00:02:07,640 需要注意switch_to这里面是在 53 00:02:07,680 --> 00:02:09,360 汇编程序里面来实现的 54 00:02:09,400 --> 00:02:10,840 那switch to会完成 55 00:02:10,880 --> 00:02:13,200 两个内核线程的切换 56 00:02:13,240 --> 00:02:14,760 我们看switch_to怎么来完成的 57 00:02:14,800 --> 00:02:15,520 那么switch_to在这个 58 00:02:15,560 --> 00:02:17,280 汇编程序switch.S里面 59 00:02:17,320 --> 00:02:20,320 可以看着跟我们刚才讲是类似的 60 00:02:20,360 --> 00:02:23,040 就是它取得了这个关于 61 00:02:23,080 --> 00:02:25,000 idle_proc它的context 62 00:02:25,040 --> 00:02:27,080 然后把它所有寄存器信息 63 00:02:27,120 --> 00:02:28,600 保存在context里面 64 00:02:28,640 --> 00:02:30,920 这是这一部分完成的工作 65 00:02:30,960 --> 00:02:34,280 OK 接下来呢是恢复init_proc 66 00:02:34,320 --> 00:02:37,680 这个线程它的相关的context 67 00:02:37,720 --> 00:02:39,360 最后一步是push eip之后 68 00:02:39,400 --> 00:02:42,320 通过ret跳转可以回到 69 00:02:42,360 --> 00:02:43,760 我们说在context里面 70 00:02:43,800 --> 00:02:46,080 保存那个eip的地址 71 00:02:46,120 --> 00:02:47,080 那是什么呢 72 00:02:47,120 --> 00:02:50,560 可以看一下在这个地址在哪 73 00:02:50,600 --> 00:02:53,080 实际上这个地址就是forkret 74 00:02:53,120 --> 00:02:56,160 forkret会进一步调用forkrets 75 00:02:56,200 --> 00:02:59,000 那它是在trapentry.S里面 76 00:02:59,040 --> 00:03:01,520 有forkrets这个入口地址 77 00:03:01,560 --> 00:03:04,640 你可以看着当我们说通过switch 78 00:03:04,680 --> 00:03:06,320 现在已经切换到我们的 79 00:03:06,360 --> 00:03:09,360 init_proc开始执行的 80 00:03:09,400 --> 00:03:10,440 那么init_proc执行 81 00:03:10,480 --> 00:03:11,680 还没到到它的入口地址 82 00:03:11,720 --> 00:03:12,800 怎么到呢 83 00:03:12,840 --> 00:03:14,480 可以看到在这里面forkrets 84 00:03:14,520 --> 00:03:17,040 再jump到trapret 85 00:03:17,080 --> 00:03:18,400 这里面是干什么事情 86 00:03:18,440 --> 00:03:20,160 这一部分是实际上是 87 00:03:20,200 --> 00:03:23,360 恢复被中断一个过程 88 00:03:23,400 --> 00:03:25,000 会被中断或者异常 89 00:03:25,040 --> 00:03:26,480 打断的过程在这儿恢复的 90 00:03:26,520 --> 00:03:29,880 当然这里面其实没有这个被打断过 91 00:03:29,920 --> 00:03:31,040 因为它第一次执行 92 00:03:31,080 --> 00:03:32,360 那这些信息从哪来呢 93 00:03:32,400 --> 00:03:35,000 我们在开始做init_proc初始化的时候 94 00:03:35,040 --> 00:03:36,040 在它的TCB里面 95 00:03:36,080 --> 00:03:38,840 就线程控制块里面把这些信息填好了 96 00:03:38,880 --> 00:03:40,840 这些信息是在trapframe里面 97 00:03:40,880 --> 00:03:42,200 把这些信息恢复之后 98 00:03:42,240 --> 00:03:44,640 会调动iret 99 00:03:44,680 --> 00:03:46,640 当iret返回之后 100 00:03:46,680 --> 00:03:48,040 它会跳到哪去呢 101 00:03:48,080 --> 00:03:49,960 跳到kernel thread entry 102 00:03:50,000 --> 00:03:52,280 这个信息是存在trapframe里面的 103 00:03:52,320 --> 00:03:53,720 那kernel thread entry会进一步 104 00:03:53,760 --> 00:03:56,080 call(调用)ebx里面代表的地址 105 00:03:56,120 --> 00:03:57,360 这个地址在哪 106 00:03:57,400 --> 00:03:59,880 其实我们说是在这个地方 107 00:03:59,920 --> 00:04:01,680 在kernel thread初始化的时候呢 108 00:04:01,720 --> 00:04:04,320 会把这个fn在这儿复制 109 00:04:04,360 --> 00:04:05,000 这就是ebx 110 00:04:05,040 --> 00:04:07,160 所以说在这里面call ebx 111 111 00:04:07,200 --> 00:04:08,640 其实就是call fn 112 00:04:08,680 --> 00:04:10,240 从而可以完成实际的 113 00:04:10,280 --> 00:04:13,320 init_proc它的一个执行 114 00:04:13,360 --> 00:04:17,920 那我们看看它在哪执行的呢 115 00:04:17,960 --> 00:04:19,120 我们再找一找 116 00:04:19,160 --> 00:04:25,840 在proc_init中 它会执行一个init main 117 117 00:04:25,880 --> 00:04:27,800 这就是那个fn 118 00:04:27,840 --> 00:04:28,880 这里面可以看着 119 00:04:28,920 --> 00:04:31,680 我们说打印出来信息就在这儿 120 00:04:31,720 --> 00:04:33,640 这和我们实际执行它 121 00:04:33,680 --> 00:04:34,800 这个结果是一样的 122 00:04:34,840 --> 00:04:35,960 那么他在哪执行呢 123 00:04:36,000 --> 00:04:37,280 其实在这儿可以看出来 124 00:04:37,320 --> 00:04:38,680 我们如果正常执行 125 00:04:38,720 --> 00:04:47,240 可以看到 OK 显示了那段信息 126 00:04:47,280 --> 00:04:53,760 这个信息就是来于init_proc来完成的 127 00:04:53,800 --> 00:04:54,520 从而可以看出来 128 00:04:54,560 --> 00:04:57,120 如果你完成了前面正确的初始化工作 129 00:04:57,160 --> 00:04:58,680 最后就可以得出这么一个 130 00:04:58,720 --> 00:04:59,400 显示一个hello world 131 00:04:59,440 --> 00:05:01,080 一个简单的内核thread 132 00:05:01,120 --> 00:05:03,040 133 00:05:03,080 --> 00:05:03,120