实验四:内核线程管理
练习1:分配并初始化一个进程控制块
首先来看几个比较重要的数据结构,kern/process/proc.h
中定义的进程控制块及kern/trap/trap.h
中定义的中断帧
struct proc_struct {
enum proc_state state; // Process state
int pid; // Process ID
int runs; // the running times of Proces
uintptr_t kstack; // Process kernel stack
volatile bool need_resched; // bool value: need to be rescheduled to release CPU?
struct proc_struct *parent; // the parent process
struct mm_struct *mm; // Process's memory management field
struct context context; // Switch here to run process即进程上下文
struct trapframe *tf; // Trap frame for current interrupt即中断上下文
uintptr_t cr3; // CR3 register: the base addr of Page Directroy Table(PDT)
uint32_t flags; // Process flag
char name[PROC_NAME_LEN + 1]; // Process name
list_entry_t list_link; // Process link list
list_entry_t hash_link; // Process hash list
}
struct trapframe {
/* below here is a struct of general registers and defined by software */
// 当中断异常发生时,此处结构内的通用寄存器信息由软件负责压栈保存
struct pushregs tf_regs;
/* below here are segement registers and defined by software */
// 当中断异常发生时,此处的段寄存器信息由软件负责压栈保存
uint16_t tf_gs;
uint16_t tf_padding0;
uint16_t tf_fs;
uint16_t tf_padding1;
uint16_t tf_es;
uint16_t tf_padding2;
uint16_t tf_ds;
uint16_t tf_padding3;
uint32_t tf_trapno;
/* below here defined by x86 hardware */
// 当中断异常发生时,此处的信息由硬件压栈保存
uint32_t tf_err;
uintptr_t tf_eip;
uint16_t tf_cs;
uint16_t tf_padding4;
uint32_t tf_eflags;
/* below here only when crossing rings, such as from user to kernel, defined by hardware */
// 仅发生特权级改变时,此处额外的信息由硬件压栈保存
uintptr_t tf_esp;
uint16_t tf_ss;
uint16_t tf_padding5;
}
struct context {
uint32_t eip;
uint32_t esp;
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
uint32_t esi;
uint32_t edi;
uint32_t ebp;
}
这里可以看到struct context
和struct trapframe
中有很多寄存器是一样的,前者用于进程上下文切换,后者用于中断上下文切换。注意这两者的含义是不一样的,在本实验中一个进程开始执行需要系统进行初始化,此时tf
被用来保存中断帧,而进程执行时是通过context
来完成切换的,详细见练习3的说明。
结构中一些重要的成员变量说明如下
mm
内存管理信息,包括内存映射列表、页表指针等state
进程所处的状态,有PROC_UNINIT
、PROC_SLEEPING
、PROC_RUNNABLE
、PROC_ZOMBIE
四种,定义在enum proc_state
中parent
父进程,在所有进程中只有内核创建的第一个内核线程idleproc
没有父进程,内核根据父子关系建立树形结构维护一些特殊的操作context
进程的上下文,保存寄存器,用于进程切换tf
中断帧,总是指向内核栈的某个位置,当进程发生中断异常,从用户跳到内核时,中断帧记录了进程在中断前的状态,由于允许中断嵌套,因此ucore
在内核栈上维护了tf
链,保证tf
总是能够指向当前的trapframe
kstack
每个线程都有内核栈,并且位于内核地址空间的不同位置,对于内核线程,该栈就是运行时程序使用的栈,对于普通进程,该栈就是发生特权级改变时需保存被打断硬件信息的栈
另外为了管理系统中所有的进程控制块,ucore
维护了一些重要的全局变量
static struct proc *current
当前占用处理机并处于运行状态的进程控制块指针static list_entry_t hash_list[HASH_LIST_SIZE]
所有进程控制块的哈希表,hash_link
将基于pid
链接入这个哈希表
根据实验手册及代码的提示,练习1的代码如下
把proc进行初步初始化(即把proc_struct中的各个成员变量清零)。但有些成员变量设置了特殊的值
// alloc_proc - alloc a proc_struct and init all fields of proc_struct
static struct proc_struct *
alloc_proc(void) {
struct proc_struct *proc = kmalloc(sizeof(struct proc_struct));
if (proc != NULL) {
proc->state = PROC_UNINIT; // 设置进程为初始态
proc->pid = -