思考题
Thinking 3.1
实验中 UVPT 往上到 ULIM 之间为进程自己的页表,即 UVPT 是页表基地址。这里访问 pgdir[PDX(UVPT)] 即为页目录中页表基地址对应的页目录项,将进程页目录的物理地址赋值到该项,即可保证能正确通过页表项虚拟地址找到物理地址。(这里保证了页目录中自映射的页目录项有效,有效即置位 PTE_V)
Thinking 3.2
map_page 的参数 data 来自 elf_load_seg 传入的参数 data,调用 elf_load_seg 时传入的 data 是加载 ELF 时的数据地址 struct Env *e。这里这个参数用于在 map_page 中获取当前需要插入新页的页目录地址和 asid,因此不能省略。
Thinking 3.3
可以根据页面加载起始地址位置和加载内容大致分为四类:
- 从页面中间加载一部分程序段内容(到页面结尾或页面中间)
- 从页面开始加载一部分程序段内容(到页面结尾或页面中间)
- 从页面开始加载一部分程序段内容后,余下部分填入0
- 从页面开始填入0(到页面结尾或页面中间)
不论从页面开始还是从页面中间,都需要新分一页来填入内容。
Thinking 3.4
这里的EPC是给CPU做处理的PC地址,CPU使用的是虚拟地址。
Thinking 3.5
- handle_int 在 kern/genex.S 中实现
- handle_mod 在 kern/genex.S 中跳转到 do_tlb_mod,在 kern/tlbex.c 中实现
- handle_tlb 在 kern/genex.S 中跳转到 do_tlb_refill,在 kern/tlb_asm.S 中实现
- handle_sys 在 kern/genex.S 中跳转到 do_syscall,在 lab4 中会实现
Thinking 3.6
enable_irq 用于开启中断:
1 | LEAF(enable_irq) // declare leaf function "enable_irq" |
timer_irq 用于禁止中断并跳转到 schedule:
1 | timer_irq: |
Thinking 3.7
硬件按照时钟每次产生时钟中断时:
- 根据 exception_handlers 的指定,是时钟中断,调用 handle_int
- 检查是否允许所有中断和是否允许时钟中断,如果允许,跳转到 timer_irq 处理时钟中断
- 暂时禁止中断,调用 schedule() ,参数 yield = 0
- 如果未调度进程 / 当前进程时间片耗尽 / 当前进程被阻塞或退出则切换进程
- 调度结束,结束中断处理,允许中断
难点分析
- env_free_list 是 LIST,对应 field 是 env_link,而 env_sched_list 是 TAILQ,对应 field 是env_sched_link,不能混用。
- env_alloc 中对没有空闲进程或调用的函数运行错误等情况一定要返回错误值,类似的,所有 int 返回值的函数对于函数内任何可能运行错误的地方都要加以判断并返回对应错误值,调用返回 int 的函数时都要判断返回值并判断是否返回错误值(可以使用 try() )
- 有些时候调用函数时会传入 int 等类型作为地址使用,这时函数要求的类型可能是 void *,如果要消除 warning 可以考虑加上类型转换再传入函数
- set a to b 的意思是 a = b,即把 b 的值赋值给 a。
- 没有 parent 的 env,parent_id 是 0。
实验感想
lab3 主要学习了进程,核心内容是对于 PCB 的操作处理,同时还简单引入了异常处理的初步流程,但是涉及到异常处理的部分更多是复制粘贴代码,后续还需要在这个基础上再多多了解一下异常处理流程(但其实完成 lab4 的学习之后已经了解了不少异常处理相关的内容)。
进程本身调度想必是很复杂的内容,在考前还需要多多复习练习读代码。