以前做MIT 6.828练习的一点总结

来源:互联网 发布:海洋cms采集资源库 编辑:程序博客网 时间:2024/05/17 06:06

以前做过mit 6.828 lab1-lab6的练习,是很老的2004版的,应某位师弟的要求贴出来,并做一点小总结,也供现在在做的兄弟一点参考。

网址是http://pdos.csail.mit.edu/6.828/2004/schedule.html

我的solution是http://download.csdn.net/source/397897,是lab6的,lab1-lab5都包含在内的。不是什么标准答案,而且很有可能某个部分有bug,做到最后可能我的确是有些虎头蛇尾,如果什么地方有问题还请告知我或与我讨论,我的email是dennis.he.2005@gmail.com。他提供的大部分的测试我应该都是测过的。另外只要是challenge的我都没有做,我开始的策略是先做完6个lab基本的exercise再做challenge的,但是真的做到后来也就犯懒了,呵呵,抱歉。兄弟们如果challenge做了的话可以到此讨论。

当时的开发环境是Red hat Enterprise。

lab1:
需要理解pc的一些基本知识,比如boot strap,实模式和保护模式下x86的寻址方式等等,另外就是熟悉一下bochs的调试环境。实际需要动手做的只有一个backtrace的函数,没什么好说的,看一看x86的calling convention,动手在bochs上调试一下就ok了。
lab2:
首先了解x86的分页管理和virtual address-->linear address-->physical address的转换过程,另外就是需要了解JOS代码中inc/pmap.h的那张表。Exercise 3是实现boot时的页表映射,这个按照那个映射表和它的提示完成就可以了;Exercise 4很讨厌,麻烦在于它与后面的关联很大,首先通过它的许多assert就需要花点功夫,另外后面的几个lab出错都常常会找错找到这里来。我个人做的时候觉得需要注意的地方是1.page_insert在处理将同一虚拟地址映射到同一个物理页面上不需要将当前已有的remove掉,但是需要修改掉permission;2. page_remove中只有当需要remove的page的refcount为0是才需要减少pte所在的page的refcount,例如同一个物理page映射为不同的virtual address的情况;3. 不管是page_insert和page_remove,只要对page table有修改,都必须tlb_invalidate一下,否则后面紧接着对内存的访问很有可能出错,这个在lab4的调试中就能看出来。
lab3:
lab3是实现用户态运行的环境。Exercise 1非常简单,只是多映射了一块UENVS的区域。Exercise 2的load_icode有点搞,首先用户程序的页目录可以直接拷贝boot_pgdir的,因为内核态使用的页表项,即0xf0000000以上的那部分映射是所有用户环境所共享的并且不会修改的,再将load用户程序需要使用的页表先建好,然后通过lcr3将页表从boot_pgdir切换到用户环境的页表,这时就可以简单地用memcpy直接拷贝用户程序的代码和数据段了。Exercise 4是实现trap的内核响应部分,需要仔细地理解x86的中断异常处理机制。需要注意的是JOS的task register是不改的,即所有task都共享同一个内核态的异常处理栈。关于其他的处理,文档和代码注释都讲得很清楚。调试得用bochs的internal debugger,vbreak 0x1b:<int 0x30代码地址>,然后跟踪异常时的情况。
lab4:
Exercise 1实现一个简单的round-robin的调度器,呵呵,应该不会有比这个更土的调度器了。Exercise 2是实现一套系统调用,主要是为了以后的fork/spawn以及文件系统做准备,如果lab2中的page_insert和page_remove做的不好,那么sys_mem_map和sys_mem_unmap会遇到很大的问题,如果测试中发现问题,可能还得回到lab2重做一遍。接下来的Part B是我觉得是这六个lab中最麻烦的。首先要想清楚他是要用用户态的程序去处理缺页异常,和平时的惯性思维有区别,整个处理的过程是 用户态发生访问异常-->内核态-->用户态的page_fault_handler处理函数-->用户态发生访问异常的地方。这个后来我想想实际上也就是linux中处理信号的流程。其中,内核态的程序需要设置好用户态page_fault_handler函数使用的栈,JOS给的是UXSTACK,在他的说明中,前5个word空着,我想是用来保存通用寄存器的,然后是eip,eflags,esp,error code和fault_va,明显,fault_va和error code是用来传给page_fault_handler的,所以用户态程序异常处理的入口的一段汇编在调用c程序之前要设置好栈正好在fault_va处。在用户态程序page_fault_handler处理中需要想清楚几个问题:1. page_fault_handler中需要有系统调用陷入到内核态,陷入到内核态之后仍然用的是内核公用栈KSTACKTOP;2. page_fault_handler中允许再次发生异常,也就是嵌套,这时内核态在传给用户态栈时需要再多预留两个word放用户态返回用户态的eip和eflags,这个在调试的时候自然会遇到;3. 如何restore eflags?用popfd/popfl,用户态如何返回用户态?当然是ret了,回忆一下x86的calling convention就可以了。另外,他留了5个word给通用寄存器,我查了资料x86的caller-save寄存器只有eax,ecx和edx,其他5个全是callee-saved,我也不知道该压哪两个合适,反正ebp和esp是绝对不需要的,我留了6个word,除了ebp和esp我都压了。Exercise 8是实现copy-on-write的,并且是用户态实现copy-on-write。首先要充分理解vpd和vpt的作用,这个是用户态访问内核态页表项的非常精巧的方式,对于一个用户态的地址va来说,vpd[va>>PDSHIFT]读出的是page table,而vpt[va>>PGSHIFT]读出的是页表项pte,vpt[va>>PGSHIFT]仅当vpd[va>>PDSHIFT]存在时才有意义,这个我想了半天才想明白,汗。其次在实现duppage中父进程不仅要将子进程的page设置为COW,还需要将自己的也设为COW。Part C是实现IPC,根据他的提示走就可以了。有意思的是那个user/prime.c测试程序,是用来找所有素数的,原理看看他给的那个plan9的链接网页,这个找素数的算法是可行,但是开销太可怕了,呵呵。
lab5:
lab5是实现一个文件系统,文件系统的meta-data非常简单,但是问题是文件系统的实现非常独特,他把整个文件系统都实现在了用户态,由一个fileserver来处理所有的文件访问请求,并利用lab4的ipc实现一个client-server的文件系统模式。他的page_cache是一个简单的映射,因为block_size和page size是一致的,并且在设计上为了简化文件最大的长度也只有4M。另外他非常奢侈地用了一个page来存放struct Fd这样几个字节的结构,实际上为了满足他的ipc的通信是一个page的要求。Exercise 7是实现spawn,与我们熟悉的exec所不一样的地方就是spawn是由父进程为子进程load code的,而exec是子进程自己来的,因为JOS的filesystem是实现在用户态的,子进程无法在用户态自己干掉自己的。我实现的lib/spawn.c没有严格的按照他的提示去做,我不管是text段,data段还是bss段都是用tmp page来copy的,图省事吧,我当时。
lab6:
lab6基本上是一个综合,以前实现上存在的bug都有可能在这里体现出来,很有可能再回到某个lab重新实现一遍代码。其他的照提示做吧,出错了狂加打印,总会找出原因的。

原创粉丝点击