那我们现在结合一下
就是实际操作来看一下
实验4所需要关注的一些细节
那实验4内核线程管理
要完成的练习呢不多
主要是能够知道
怎么去分配一个进程控制块
以及怎么能够去
调度 执行一个内核线程
那在这里面呢有三个练习
练习1 练习2 练习3
其实应对的呢
应对的就是主要是在哪呢
在这个代码里面的这个地方
就是proc.c
proc.c里面呢有两个函数需要关注
一个是叫做proc_init这么一个函数
它会设置idle_proc
并进一步去创建init_proc
就第0号线程和第1号线程
然后呢第二个是do_fork
这个函数会完成具体的
特别是针对init_proc
一个它的内部线程控制块
一个初始化工作
然后呢当把这些初始化完毕之后
我们可以看到在我们的总控程序
就是ucore里面总控函数
code_init里面呢
会去进一步去调这个cpu_idle
cpu_idle就是去查找
当前这个idle_proc
它是不是need reschedule
其实我们前面已经做过介绍
初始化是need reschedule是等于1的
如果等于1表明它可以被切换掉
换下一个线程去执行
那么会调schedule 39
schedule也就是说会把
我们刚才说的那个线程那个队列
线程控制块的队列呢
找出对应的一个
处于ready态一个线程去执行它
当然这里面我们细节不用看（lab6会详细分析）
大致可以看着在这里面函数proc_run
proc_run它可以调switch_to
我们后面讲到过的
关于怎么去把它的内核的栈
它的页表相关的页目录表的基址寄存器
以及它的context做一个切换
做完这个之后在switch_to
需要注意switch_to这里面是在
汇编程序里面来实现的
那switch to会完成
两个内核线程的切换
我们看switch_to怎么来完成的
那么switch_to在这个
汇编程序switch.S里面
可以看着跟我们刚才讲是类似的
就是它取得了这个关于
idle_proc它的context
然后把它所有寄存器信息
保存在context里面
这是这一部分完成的工作
OK 接下来呢是恢复init_proc
这个线程它的相关的context
最后一步是push eip之后
通过ret跳转可以回到
我们说在context里面
保存那个eip的地址
那是什么呢
可以看一下在这个地址在哪
实际上这个地址就是forkret
forkret会进一步调用forkrets
那它是在trapentry.S里面
有forkrets这个入口地址
你可以看着当我们说通过switch
现在已经切换到我们的
init_proc开始执行的
那么init_proc执行
还没到到它的入口地址
怎么到呢
可以看到在这里面forkrets
再jump到trapret
这里面是干什么事情
这一部分是实际上是
恢复被中断一个过程
会被中断或者异常
打断的过程在这儿恢复的
当然这里面其实没有这个被打断过
因为它第一次执行
那这些信息从哪来呢
我们在开始做init_proc初始化的时候
在它的TCB里面
就线程控制块里面把这些信息填好了
这些信息是在trapframe里面
把这些信息恢复之后
会调动iret
当iret返回之后
它会跳到哪去呢
跳到kernel thread entry
这个信息是存在trapframe里面的
那kernel thread entry会进一步
call(调用)ebx里面代表的地址
这个地址在哪
其实我们说是在这个地方
在kernel thread初始化的时候呢
会把这个fn在这儿复制
这就是ebx
所以说在这里面call ebx 111
其实就是call fn
从而可以完成实际的
init_proc它的一个执行
那我们看看它在哪执行的呢
我们再找一找
在proc_init中 它会执行一个init main 117
这就是那个fn
这里面可以看着
我们说打印出来信息就在这儿
这和我们实际执行它
这个结果是一样的
那么他在哪执行呢
其实在这儿可以看出来
我们如果正常执行
可以看到 OK 显示了那段信息
这个信息就是来于init_proc来完成的
从而可以看出来
如果你完成了前面正确的初始化工作
最后就可以得出这么一个
显示一个hello world
一个简单的内核thread