BUAA-OS-2023-Lab3-Report
BUAA-OS-2023-Lab3-Report
Thinking 3.1
- Thinking 3.1 请结合MOS 中的页目录自映射应用解释代码中
e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) PTE_V
的含义
根据自映射机制可知, e->env_pgdir[PDX(UVPT)]
指的是进程块 e
的指向用户页表的页目录项,又根据页表映射的要求,这一项的内容应该是用户页表的物理地址,即PADDR(e->env_pgdir)
,再为其置有效位为1,初始化用户页表
Thinking 3.2
elf_load_seg()
以函数指针的形式,接受外部自定义的回调函数map_page
。请你找到与之相关的data
这一参数在此处的来源,并思考它的作用。没有这个参数可不可以?为什么?elf_load_seg()
函数定义位于lib/elfloader.c
中;使用仅有一处,位于kern/env.c
的load_icode()
函数中
首先这里的回调函数是定义在 kern/env.c
的 load_icode_mapper()
函数,作用是把一段虚拟地址的内容加载到某个进程管理块对应进程的虚拟内存中(通过申请物理页面并建立页表映射)。这两个函数中的 data
参数相同,其来源是待加载的进程管理块指针,它用来告知 load_icode_mapper()
函数加载到哪个进程。所以不可以没有这个参数,如果没有这个参数,将无法确定加载到哪个进程的虚拟内存中。
在 elf_load_seg()
中使用回调函数,可以令用户自定义加载段中各个待加载页面的方式
Thinking 3.3
- 结合
elf_load_seg()
的参数和实现,考虑该函数需要处理哪些页面加载的情况
考虑情况如下:
- 段起始地址未页面对齐
u_long offset = va - ROUNDDOWN(va, BY2PG); |
- 复制段大小不足一个页面
map_page(data, va, offset, perm, bin, MIN(bin_size, BY2PG - offset)) |
- 为
.bss
段预留空间:文件大小与程序大小不同需要补充空页面
while (i < sgsize) { |
Thinking 3.4
这里的
env_tf.cp0_epc
字段指示了进程恢复运行时 PC 应恢复到的位置。我们要运行的进程的代码段预先被载入到了内存中,且程序入口为e_entry
,当我们运行进程时,CPU 将自动从 PC 所指的位置开始执行二进制码。
- 思考上面这一段话,并根据自己在 Lab2 中的理解,回答:你认为这里的
env_tf.cp0_epc
存储的是物理地址还是虚拟地址?
显然, PC 值应该在程序运行过程中保持连续,而程序运行时的物理地址应该是经过页表映射后不连续的,虚拟地址可以则保证其连续性。所以这里的 env_tf.cp0_epc
应该保存的是虚拟地址
Thinking 3.5
在异常向量组中,通过把相应处理函数的地址填到对应数组项中,我们初始化了如下异常: 0 号异常的处理函数为
handle_int
,表示中断,由时钟中断、控制台中断等中断造成 1 号异常的处理函数为handle_mod
,表示存储异常,进行存储操作时该页被标记为只读 2 号异常的处理函数为handle_tlb
,表示 TLB load 异常 3 号异常的处理函数为handle_tlb
,表示 TLB store 异常 8 号异常的处理函数为handle_sys
,表示系统调用,用户进程通过执行syscall
指令陷 入内核
- 试找出上述 5 个异常处理函数的具体实现位置。
在 kern/genex.S
文件中,我们可以先找到其上声明的 handle_int
函数:
NESTED(handle_int, TF_SIZE, zero)# handle_int 实现 |
其余的四个函数通过同文件的宏 BUILD_HANDLER exception handler
定义在其后:
.macro BUILD_HANDLER exception handler |
Thinking 3.6
- 阅读 init.c、kclock.S、env_asm.S 和 genex.S 这几个文件,并尝试说出
enable_irq
和timer_irq
中每行汇编代码的作用
enable_irq
LEAF(enable_irq) |
timer_irq
timer_irq: |
Thinking 3.7
- 阅读相关代码,思考操作系统是怎么根据时钟中断切换进程的
首先,我们在kern/kclock.S 中的kclock_init 函数完成了时钟中断的初始化,并在 genex.S 中的 enable_irq 中设置允许响应该中断
当时钟计时归零(时间片耗尽),产生时钟中断,进入异常处理程序,并跳转到 handle_int 处理中断。
当前我们的系统只能处理 timer_irq
一种时钟中断,所以直接进入 timer_irq
函数,恢复时钟,并执行 schedule(0);
在 schedule()
中,我们根据当前进程块状态进行进程块队列调度,实现进程切换或进程的舍弃,至此完成时钟中断响应