OS LAB3 实验报告
思考题
thinking3.1
Thinking 3.1 请结合 MOS 中的页目录自映射应用解释代码中 e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_V 的含义。
对于每个env,需要保证的是每个线程尽管拥有不同的地址空间,envs、pages、pgdir都处在自身虚拟地址空间的相同位置,对于页目录,这要求pgdir能够自映射。在env_setup_vm
中,应将UVTP起始的页面映射为该进程分配到的页面,故在进程页目录中将虚拟地址空间的UVPT的对应的页目录项设置为该进程分配到的页目录所在的实际位置,从而使该进程可以用虚拟地址UVPT访问到页目录自身。
之所以这一步要在虚拟地址空间初始化中单独设置,是因为不像pages和envs位于静态的位置,每个进程分配到的页表页位置是不同的、动态的,需要通过该初始化函数设置正确的映射。
thinking3.2
Thinking 3.2 elf_load_seg 以函数指针的形式,接受外部自定义的回调函数 map_page。 请你找到与之相关的 data 这一参数在此处的来源,并思考它的作用。没有这个参数可不可 以?为什么?
data的作用是传入目标env的指针,从而在进一步调用load_icode_mapper
的过程中可以拿到env地址,并在env对应的页表中映射加载的页面。
thinking3.3
Thinking 3.3 结合 elf_load_seg 的参数和实现,考虑该函数需要处理哪些页面加载的情 况。
- 需要判断va是否页面对齐,如果不对齐则要对起始页面单独处理。
- 需要处理ph的文件大小和mem大小不同的情况,如果不同需要先复制文件范围内对应的数据到对应位置,再直接分配其他页面,跳过复制步骤。
thinking3.4
Thinking 3.4 思考上面这一段话,并根据自己在 Lab2 中的理解,回答: • 你认为这里的 env_tf.cp0_epc 存储的是物理地址还是虚拟地址?
对于每个程序而言,它拥有完整的虚拟地址空间,编译过程也与实际系统运行状态无关。故入口保存的位置应当是虚拟地址。
thinking3.5
Thinking 3.5 试找出 0、1、2、3 号异常处理函数的具体实现位置。8 号异常(系统调用) 涉及的 do_syscall() 函数将在 Lab4 中实现。
0号异常是中断异常,函数位于genex.S中。
handle_mod, handle_tlb, handle_sys通过genex.S中定义的宏来实现,跳转到do_tlb_mod,do_tlb_refill和do_syscall。
thinking3.6
阅读 init.c、kclock.S、env_asm.S 和 genex.S 这几个文件,并尝试说出 enable_irq 和 timer_irq 中每行汇编代码的作用。
|
|
|
|
thinking3.7
阅读相关代码,思考操作系统是怎么根据时钟中断切换进程的。
每当发生时钟中断时,内核进入中断处理程序,查看是不是IM4时钟中断,如果是的话则跳转到时钟中断处理程序。在此,如3.6分析,会首先响应时钟中断,随后进入schedule函数中进行进程调度。
在env_sched_list中保存着所有等待调度的程序。
在函数中,如果当前进程没有主动转移控制权或者耗尽时间片或停止运行,则继续运行当前进程。
如果是第一次调用,则取list头部的进程开始运行。
如果其他情况,将list头部的当前进程放到list尾部,随后取头部进程运行。
如果list空了,则panic错误。
实验难点
-
异常处理的流程理解有一定难度
- genex.S、使用宏来批量定义中断处理,并对外提供了定义好的函数。
- 在traps.c中,整合了genex.S中定义的处理函数并保存映射到exception_handlers中,向外提供。
- 在entry.S中,exc_gen_entry中根据中断原因跳转到对应的exception_handlers[CP0_CAUSE[6:2]]中进行处理。
-
进程调度的细节处理需要格外注意。特别是从TAILQ中移除元素要在加入元素之前,因为在加入到list过程中会修改item对应的link域,如果此时item已经在list中则会导致一系列问题。
之所以能够保证、sched_list中的均为RUNNABLE,是依赖于在schedule的过程中如果当前进程不是RUNNABLE则删除后不会插入到队尾。
-
要注意注释中如何要求代码遇到异常后的处理。如Exercise3.4中env_setup_vm中的page_alloc有导致reture非零值,这需要我们在调用时用try包裹以进行处理。
实验体会
- 在本单元的实验中大量设计了汇编和c语言的交叉调用,这使得理解上有很大难度,特别是异常处理流程在汇编代码和c代码之间反复调用,这使得充分理解调用关系更为困难。
- 在完成代码及需求的过程中要首先对于整个系统有充分的认识,这样才能避免出现写出了能跑通的代码却不知道为什么能跑通这种尴尬的局面。