其他分享
首页 > 其他分享> > 北航操作系统课程lab4实验报告

北航操作系统课程lab4实验报告

作者:互联网

OS lab4实验报告

实验思考题

Thinking 4.1

思考并回答下面的问题:

 

 

Thinking 4.2

思考下面的问题,并对这个问题谈谈你的理解: 请回顾 lib/env.c 文件中 mkenvid() 函数的实现,该函数不会返回 0,请结合系统调用和 IPC 部分的实现 与 envid2env() 函数的行为进行解释。

 

我们可以看到该函数为:

 u_int mkenvid(struct Env *e) {
     u_int idx = e - envs;
     u_int asid = asid_alloc();
     return (asid << (1 + LOG2NENV)) | (1 << LOG2NENV) | idx;
 }

显然,无论如何第11位的值都为1,函数不会返回0,并且由此可得,根进程的进程号为0x400,这也与实验结果相符合。

 

Thinking 4.3

思考下面的问题,并对这两个问题谈谈你的理解:

 

Thinking 4.4

关于 fork 函数的两个返回值,下面说法正确的是:

A、fork 在父进程中被调用两次,产生两个返回值

B、fork 在两个进程中分别被调用一次,产生两个不同的返回值

C、fork 只在父进程中被调用了一次,在两个进程中各产生一个返回值

D、fork 只在子进程中被调用了一次,在两个进程中各产生一个返回值

 

说法正确的是C

 

Thinking 4.5

我们并不应该对所有的用户空间页都使用duppage进行映射。那么究竟哪些用户空间页应该映射,哪些不应该呢? 请结合本章的后续描述、mm/pmap.c 中 mips_vm_init 函数进行的页面映射以及 include/mmu.h 里的内存布局图进行思考。

 

在0 ~ USTACKTOP范围的内存需要进行映射,其上范围的内存要么属于内核,要么是所有用户进程共享的空间,用户模式下只可以读取。可写但不共享的页面都需要设置PTE_COW进行保护。

 

Thinking 4.6

在遍历地址空间存取页表项时你需要使用到vpd和vpt这两个“指针的指针”,请参考 user/entry.S 和 include/mmu.h 中的相关实现,思考并回答这几个问题:

 

 

Thinking 4.7

page_fault_handler 函数中,你可能注意到了一个向异常处理栈复制Trapframe运行现场的过程,请思考并回答这几个问题:

 

 

Thinking 4.8

到这里我们大概知道了这是一个由用户程序处理并由用户程序自身来恢复运行现场的过程,请思考并回答以下几个问题:

 

 

Thinking 4.9

请思考并回答以下几个问题:

 

实验难点展示

系统调用

系统调用的函数十分简单,以前写了page_alloc,现在继续写mem_alloc,每次都是用一个if语句赋值给一个整形值,判断是否小于零,是的话就返回这个值,这些负数值都是有宏定义的,可以去触发一些异常。

我想说的是syscall.S这个函数的编写,往年貌似就考过这个。这个函数干的事情很明显的揭示了系统调用是如何将用户态的数据跨越到内核态的。值得一提的是在Mars里面编程的系统调用号寄存器为$v0,然后我就复制了这个寄存器,后面是一行一行对着别人的代码才看出来的,在这个操作系统中,系统调用号就是第一个参数对应的寄存器$a0,我们需要把这个寄存器读出来,才能知道是什么系统调用,才能运行对应的系统调用程序(通过jal t2实现,而t2又是由$a0决定的)。系统调用表就在下方sys_call_table处,和lab3处理多种中断的方式一模一样

 lw a0, TF_REG4(sp)
 •    lw a1, TF_REG5(sp)
 •    lw a2, TF_REG6(sp)
 •    lw a3, TF_REG7(sp)
 •    addiu sp, sp, -24
 •    sw t3, 16(sp)
 •    sw t4, 20(sp)   
 ​
 •    jalr    t2                          // Invoke sys_* function
 •    nop

 

(图源PPT)

用户态与内核态函数

这个很容易把人搞晕的,我只能说在lib里的都是内核态函数,在user里的都是用户态函数,内核态函数sys_*,用户态函数是syscall_*。我们补充的几乎都是内核态的系统调用函数(lib/syscall_all.c),而用户态的系统调用函数全是直接调用msyscall(user/syscall_lib.c)。

在做课上实验的时候,实现一个新的系统调用基本上是四步走:

  1. 在头文件中先定义好新的系统调用号系统调用名

  2. 用户态先定义这个syscall_*函数,调用msyscall

  3. 在syscall.S中添加上新的系统调用名

  4. 在内核空间实现这个系统调用sys_*,这才是实现该系统调用功能的主要步骤。

进程间通信

在lab 4-1-Extra中实现了对发送进程的阻塞,这避免了CPU的轮询。最近几次课上都考到了类似于PV操作的知识,而在我看来PV操作与信号量相关的一部分知识是操作系统理论课最灵活的部分了。PV操作本质是对一个整形的信号量进行加加减减的操作,实现起来也是如此。

一个进程能够接收信息之前,可以有多个进程准备发送消息,但他们必须处于阻塞状态,因为要接受消息的进程还没有准备好接收。而若是出现这种情况,接收进程可以直接接收消息,并不再过渡到NOT_RUNNABLE状态。

实现的大致思路是需要建一个结构体来存储要发送进程的信息,在发送进程碰壁时,设置为NOT_RUNNABLE状态,并直接yield,要注意,这个函数会直接导致该进程当前执行的函数return,所以只有在sys_ipc_recv函数中直接接收消息。

创建子进程

这部分是我认为lab 4中最难的。

父进程要做的事:

  1. 设置缺页中断处理函数入口地址

  2. 使用syscall_env_alloc创建子进程

  3. 使用duppage设置PTE_COW标识,并将子进程的虚拟空间和父进程的虚拟空间联合起来,指向相同的物理页面

  4. 为子进程分配异常处理栈,为子进程设置状态

子进程要做的事:

  1. 设置子进程所代表的env指针

  2. 子进程在缺页中断时陷入内核,再返回到设置好的缺页中断处理函数中用户态下的pgfault函数,拷贝完页面后,返回到用户态的中断现场。

缺页中断的处理:

  1. 陷入内核,执行page_fault_handler(lib/trap.c),保存现场并设置异常处理栈,这里面设置了EPC的值tf->cp0_epc = curenv->env_pgfault_handler;

  2. 这个值其实就是__asm_pgfault_handler(user/entry.S)这个函数,lw t1, __pgfault_handler jalr t1,它调用了pgfault函数,缺页处理,拷贝页面。

  3. 然后返回到__asm_pgfault_handler函数中,通过使用lw指令将栈中保存的值放回寄存器中,保存现场,返回到中断现场。

体会与感想

lab 4在思维要求上比前几个实验更难了,最主要做的就是两个部分,内核函数和用户函数的相结合、缺页中断的设置与处理。

一方面,脑子里一定要清楚现在是在改内核还是改用户,因为之前写的都是内核,而内核函数是不能在用户空间调用的。以前写过的page_insert呀,envid2env呀都不能用在用户空间了,只能用user_bcopy和user_bzero来代替bcopy和bzero了。现如今看来,从用户空间到内核空间可以是系统调用或者中断异常的形式,系统调用将环境上下文保存在KERNEL_SP中,中断异常将环境上下文保存在TIMESTACK中。

另一方面,缺页中断是父子进程间实现的一个重要的功能,但我印象最深的是父子进程调用fork产生不同的返回值的实现,这是在int sys_env_alloc(void)(lib/syscall_all.c)中实现的。将父进程的epc设置为子进程的pc值,这让子进程第一次执行时就能够接着调用fork的后一条指令。父进程直接返回子进程进程号,子进程则将0这个返回值保存在PCB里,轮到子进程执行时直接把$v0寄存器设置为0。

写的代码越多,bug也就越多,我课下遇到的一个bug是在fork时会创建两个进程,加上父进程一共三个,试了很久才发现是env_run这个函数的锅,十分的难受。

遗留难点

关于envid2env这个函数里的checkperm参数,这个用于检查是否是当前进程或当前进程子进程的参数显得很莫名其妙,我大致看了一下,只有在删除一个进程中会将这个参数置1 ,或许是为了确保一个进程只能由自己或自己的父进程结束。其他地方应该都是设置为0的。而经过实验发现,如果在补全代码的时候没有设置为0,很可能还过不了课下。

创建一个新进程需要设置堆栈,设置异常处理程序入口,设置页表,分配页面,拷贝页面,设置pc等等操作,太多太杂了,感觉很难整合起来,而且也很容易忘。

标签:调用,函数,北航,用户,内核,lab4,进程,实验报告,页表
来源: https://www.cnblogs.com/emodiary121/p/16306158.html