相关数据结构
在实现和进程相关的代码之前,我们先来实现一些数据结构来帮助我们对于进程进行管理。在本章实现的进程管理模型中,我们主要维护两个数据结构:进程控制块和进程上下文。进程控制块维护进程的各个信息,包括内存映射,进程名等等。进程上下文里面保存了和进程运行状态相关的各个寄存器的值,这些是为了之后恢复进程运行状态用的。下面我们来看一看这两个数据结构。
进程控制块
我们在ucore中使用结构体struct proc_struct
来保存和进程相关的控制信息。
struct proc_struct
内部结构如下:
这里面值得我们关注的主要有以下几个成员变量:
parent
:里面保存了进程的父进程的指针。在内核中,只有内核创建的idle进程没有父进程,其他进程都有父进程。进程的父子关系组成了一棵进程树,这种父子关系有利于维护父进程对于子进程的一些特殊操作。mm
:这里面保存了内存管理的信息,包括内存映射,虚存管理等内容。具体内在实现可以参考之前的章节。context
:context
中保存了进程执行的上下文,也就是几个关键的寄存器的值。这些寄存器的值用于在进程切换中还原之前进程的运行状态(进程切换的详细过程在后面会介绍)。切换过程的实现在kern/process/switch.S
。tf
:tf
里保存了进程的中断帧。当进程从用户空间跳进内核空间的时候,进程的执行状态被保存在了中断帧中(注意这里需要保存的执行状态数量不同于上下文切换)。系统调用可能会改变用户寄存器的值,我们可以通过调整中断帧来使得系统调用返回特定的值。cr3
:cr3
寄存器是x86架构的特殊寄存器,用来保存页表所在的基址。出于legacy的原因,我们这里仍然保留了这个名字,但其值仍然是页表基址所在的位置。
进程上下文
进程上下文使用结构体struct context
保存,其中包含了ra
,sp
,s0~s11
共14个寄存器。
可能感兴趣的同学就会问了,为什么我们不需要保存所有的寄存器呢?这里我们巧妙地利用了编译器对于函数的处理。我们知道寄存器可以分为调用者保存(caller-saved)寄存器和被调用者保存(callee-saved)寄存器。因为线程切换在一个函数当中(我们下一小节就会看到),所以编译器会自动帮助我们生成保存和恢复调用者保存寄存器的代码,在实际的进程切换过程中我们只需要保存被调用者保存寄存器就好啦!
下一小节,我们来看一看到底如何进行进程的切换。
最后更新于