从mykernel来分析linux系统的启动过程

来源:互联网 发布:如何申请淘宝小号 编辑:程序博客网 时间:2024/06/05 02:31

徐晨 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000


我们本次分析一个精简过的内核代码mykernel,配合kernel3.9.4使用,该代码来自https://github.com/mengning/mykernel,源码的README中已经详细说明了如何下载源码,打补丁,编译,以及在虚拟机下运行该内核镜像的方法。源码中指出:
“您只要在mymain.c基础上继续写进程描述PCB和进程链表管理等代码,在myinterrupt.c的基础上完成进程切换代码,一个可运行的小OS kernel就完成了。”

孟老师在课程公告中对mykernel做了简明的讲解:
“理解和运行mykernel,它是提供初始化好的CPU从my_start_kernel开始执行,并提供了时钟中断机制周期性执行my_time_handler中断处理程序,执行完后中断返回总是可以回到my_start_kernel中断的位置继续执行。当然中断保存现场恢复现场的细节都处理好了,mykernel就是一个逻辑上的硬件平台,具体怎么做到的一般不必深究。”

这里主要分析一下mymain.c和myinterupt.c中的相关代码是如何在kernel启动中发挥作用的。

首先,我们从pcb.h谈起:


该头文件中定义了一个两个结构体,第一个是struct Thread,它用来存储线程的ip和sp。另外一个是PCB(进程控制块),这是操作系统中一个很重要的数据结构,所有关于进程的信息都存储在PCB中,这里代码中简化了PCB的结构,只给出了一些最基本的信息,如Pid(进程号),进程状态,程序入口地址,内核堆栈,线程信息等。

有了PCB这个数据结构,我们就可以用它来创建进程或者在进行进程间切换时保存信息。

我们再来看mymain.c文件,mykernel内核是从my_start_kernel函数开始执行的,所以我们在该函数中首先创建进程,创建进程的方法就是填充PCB的信息。


我们可以从代码中看出,这里讲PCB实现成了一个环形队列,每次创建新的进程时,都将该进程插入到队列的最后一个位置,然后将该进程指向第一个进程。创建好进程之后,该函数中最重要的一段代码出现了,该部分利用嵌入式汇编来启动0号进程。


代码首先将PCB中的sp信息放入esp寄存器,然后将ebp压栈(因为此时进程堆栈为空栈,所以压入esp相当于ebp),第三第四句我们将eip压栈然后ret(即pop eip),下一条指令就会跳转到0号进程的第一条指令开始执行,在这里就是my_process()。

my_process干了一件很简单的事情,就是循环等待并print进程id,然后观察变量my_need_sched是否为1,如果是则主动进入调度函数my_schedule开始执行。


my_schedule函数在myinterupt.c中,在分析该函数之前,我们先看一下该文件中的另一个函数,my_timer_handler。


mykernel提供了时钟中断机制,周期性执行my_time_handler中断处理程序,该函数定时观察my_need_sched是否不等于1,如果是则将其置为1,使myprocess执行my_schedule()。

这时我们分析一下my_schedule,该函数在PCB环形队列中选择下一个进程进行执行,但对于处于不同状态的进程,调度方式略有不同,如果即将上CPU的进程之前已经运行过(即state为0),我们保存当前进程的信息,然后把下一个进程的信息写入到寄存器中,执行ret使下一个进程开始执行。


之前没有在运行态的(state不为0),我们先将其设置为运行态,我们这里需要初始化其ebp,因为该进程的堆栈是空栈esp=ebp。


至此,我们的mini kernel的运行分析完毕。
0 0
原创粉丝点击