操作系统实验
Lab4
目录
1.思考题解答
2.难点分析
3.思考体会
思考题解答
Thinking 4.1
- 系统从用户态切换到内核态后,内核首先需要将原用户进程的运行现场保存到内核空间(在 kern/entry.S 中通过 SAVE_ALL 宏完成),其中保存的信息包括通用寄存器的值。
- 可以,操作系统在陷入内核的过程中并未对寄存器 a0 ~ a3 存储的值作修改。
- 传入 msyscall 函数的参数被保存在寄存器 a0 ~ a3 中和栈中,从用户态转移到内核态调用 sys 开头的参数时,用户态的运行现场保存在 Trapframe 中被传递给内核,其中包含寄存器的值和栈帧,sys 开头的函数可根据这些信息确定传入的参数,与传入 msyscall 函数的参数相同。
- 对 EPC 的值做了处理,将函数返回值填入 v0 寄存器中,这样使得返回用户态后程序能从正确的位置正常运行下去。
Thinking 4.2
判断 e->env_id != envid 的情况时为了防止取到一个未经 env_alloc 的空进程。如果没有这步判断,可能会在 env 数组中取到一个未经初始化分配的进程。
Thinking 4.3
kern/env.c 文件中 mkenvid() 不会返回 0:
u_int mkenvid(struct Env *e) {
static u_int i = 0;
return ((++i) << (1 + LOG2NENV)) | (e - envs);
}
其中 ++i 保证了 env_id 不会是0。
在 envid2env() 函数中:
if(envid == 0){
*penv = curenv;
return 0;
}
可见,当envid的值是0时,函数会直接返回指向当前进程控制块的指针;因此,将 0 传入函数可以方便地访问当前进程的控制块。
Thinking 4.4
C
由代码顺序执行特征和实验结果可知,fork() 函数在父进程中被调用了一次,创建子进程后在两个进程中分别返回。
Thinking 4.5
- ULIM 和 UTOP 之间存储的是内核页表和进程控制快等信息,这部分在 env_init 中通过 map_segment 函数初始化了,不必复制。
- UTOP 和 USTACKTOP 之间是为异常处理预留的空间,不必复制。
- USTACKTOP 之下为用户常规使用空间,需要复制。
Thinking 4.6
#define vpt ((volatile Pte *)UVPT)
#define vpd ((volatile Pde *)(UVPT + (PDX(UVPT) << PGSHIFT)))
- vpt 是指向页表地址的指针,vpd 是指向页目录的指针。需要访问页表或页目录中第 n 项时,只需使用 vpt[n] 或 vpd[n] 即可。
- 由宏定义知 vpt 和 vpd 都对应了相应区域的首地址,且有强制转换为其赋予种类,则进程可以十分方便的将其视作数组首地址来访问。
- 观察知 vpd 的值是在 vpt 与 vpt + 4MB 之间的,即页目录首地址通过自映射(计算 vpd 之前的页数进行转换加和)映射到页表中的一页。
- 不能;页表需在内核态维护,用户态只读。
Thinking 4.7
- syscall_set_tlb_mod_entry 函数可被传入用户定义的异常处理函数指针,若自定义的异常处理函数在运行时又对一个 COW 页进行写入,则需要采用异常重入策略。
支持异常重入增强了程序的扩展性,保证用户自定义异常处理函数的安全性。 - 因为指导书中说明异常处理在用户态进行,若需要访问内核态的现场,则需通过复制 Trapframe 访问。
Thinking 4.8
微内核体系下,用户态进行页面处理更加方便。减下了内核出错的概率,提高系统执行效率和稳定性。
Thinking 4.9
- 将 syscall_set_tlb_mod_entry 的调用放置在 syscall_exofork 之前充分保证了写时复制机制建立之前就存在写入异常处理机制,保证了系统的安全性。
- 如果放置在写时复制保护机制完成之后,此时异常处理函数未被制定,异常处理机制无法使用。
难点分析
本次实验的难点主要有以下几个方面:
- 理解系统调用的过程,理清系统调用间的函数关系和内存保护注意事项。
- 理清 fork 机制建立的过程:
- 申请进程空间
- 拷贝父子进程数据
- 对 COW 的处理
- 对进程运行队列的维护
- 理清异常处理机制的建立与写时复制机制间的关系。
思考体会
本次作业模块间的耦合度要比以往高出许多,特别是 fork 模块的实现,需要考虑到父子进程地址空间间的关系和一系列异常处理措施。
本次作业对 bug 的查找也十分有挑战性,笔者采取的方法是使用之前实现的 printk 函数对进程的运行情况进行监测。