刘畅 原创作品转载请注明出处 《Linux内核分析》MOOC课程
http://mooc.study.163.com/course/USTC-1000029000
写在前面
本实验是模拟Linux内核的工作过程,包括进程上下文的切换和基于时间片轮转的进程调度。在实验平台的基础上,简要的完成一个简单的多道程序内核代码。
进程的描述
在这个实验中,在mypcb.h中,用结构体PCB描述进程的特性,比如进程id、进程的栈、进程内的线程等,这个结构体是一个链表形式组织的。其中进程中的线程用Thread结构体描述,包含栈顶地址和指令指针的地址。如下图所示:
进程的初始化
进程的初始化 :在mymain.h中对进程进行了初始化,即把进程的pid、进程中线程的栈顶指针初始化以及组织成一个进程链表形式,并且把每个进程的入口地址都指向my_process函数。如下图所示:
0号进程 :首先执行的是pid为0的进程,然后通过内嵌汇编的形式交给CPU执行,它将当前进程的栈顶地址放到esp寄存器中,然后将要调度进程的栈帧和进程的ip保存到栈中,通过弹栈ret的方式,将eip指向要调度进程的入口地址(my_process函数)。如下图所示:
my_process函数: 这个函数不停的加1,直到加到结果为10000000的时候,将调度状态改为0,打印当前进程的信息,并通过my_schedule函数实现进程的调度。
进程的调度
my_schedule函数在myinterrupt.c中实现的,它找到进程链表中的下一个进程,进行调度。
如果即将调度的进程从未运行过,那么需要为新进程构造一个堆栈,即把esp和ebp的寄存器的值,全都置为新进程的sp所指的地方。如下图所示:
如果即将调度的进程之前运行过,那么它的栈底指针ebp的值保存在栈中了,就不需要对其赋值了。
进程切换时,需要将
正在运行的进程的栈底指针ebp保存在栈中 ,将栈顶指针esp和当前指针指针寄存器eip的值保存在PCB结构体中。然后把下个进程的esp和eip放到对应的寄存器中。如下图所示:
运行结果
实验总结
这个代码虽然简陋,但其体现了Linux内核中进程启动和切换的过程。
进程的启动 :
使用内嵌汇编的形式,为待运行的进程开辟栈空间,并让CPU的eip寄存器指向进程的入口点。
进程的切换 :
先保存进程的上下文,即栈顶指针esp的值和eip寄存器的值,这些信息是存放在PCB中的,而栈底指针ebp的值是放到栈中的。
并依据进程调度算法,选出下一个要调度的进程,判断待调度的进程之前是否运行过。若运行过就需要通过弹栈的方式找到它的栈帧ebp,若没运行过,直接将PCB中记录的sp的值放到ebp中。然后将待调度进程的运行点(入口点)放到eip中,这是通过先把ip值压到栈中,再ret的形式实现的。因为我们是无法直接修改eip值的。
ret 指令相当于 pop eip
刘畅 原创作品转载请注明出处 《Linux内核分析》MOOC课程
http://mooc.study.163.com/course/USTC-1000029000