Lab3实验报告
一、思考题
Thinking 3.1
答:
UVPT位于kuseg中,是在0x7fc0 0000 - 0x8000 0000的大小为4MB的区域,其作用是保存用户进程页表信息。PADDR(e->env_pgdir)是进程页目录所在物理地址,e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_V代表页目录中第PDX(UVPT)项映射到了页目录本身的地址,因此代码实现了自映射。
Thinking 3.2
答:
data 在函数load_icode_mapper和elf_load_seg中用到。
作用:通过该指针可以得到页目录的的虚拟地址,从而将ELF加载到合适位置。
没有这个参数不可以。
这个参数联系了load_icode_mapper、elf_load_seg和load_icode三个函数,这个参数可以使elf_load_seg更加灵活,可以调用不同的map_page函数从而实现不同操作。而这些不同的map_page函数可能调用不同的数据辅助映射,而由于data是 void *类型,可以强制转换同时没有影响。
Thinking 3.3
va与va+bin_size相对位置如下
va+bin_size与va+sg_size相对位置如下
Thinking 3.4
答:
虚拟地址。
Thinking 3.5
handle_int位于kern/genex.S中通过阅读代码可知,
BUILD_HANDLER是一个可以用于定义异常处理函数的宏。1
2
3
4
5
6
7.macro BUILD_HANDLER exception handler
NESTED(handle_\exception, TF_SIZE, zero)
move a0, sp
jal \handler
j ret_from_exception
END(handle_\exception)
.endm其中
exception是handle_()括号中内容,handler是引用的处理函数。
1 | BUILD_HANDLER tlb do_tlb_refill |
根据以上代码可以知道其余异常处理函数的实现位置
handle_mod引用的函数do_tlb_mod定义在kern/tlbex.c中handle_tlb引用的函数do_tlb_refill定义在kern/tlb_asm.S中handle_sys引用的函数do_syscall是系统调用函数。
Thinking 3.6
enable_irq代码和分析如下
1 | LEAF(enable_irq) |
timer_irq代码和分析如下
1 | timer_irq: |
Thinking 3.7
实验设置静态变量count记录当前进程curenv剩余的时间片数量。每过一个时钟中断count--代表其消耗了一个时间片,当count等于0时代表这一进程的时间片已经耗尽,触发进程切换。系统保存上下文,跳转至schedule函数。
schedule函数进行判断:
若参数yield为0且进程时间片没有耗尽且进程是RUNNABLE状态,则继续执行该进程。
若参数yield不为0或进程时间片耗尽或进程不是RUNNABLE状态,此时如果当前有进程执行,则将其插入队尾,等待之后执行。之后选择队列中第一个进程,将count设置为其优先级(即env_pri),调用env_run使其运行。
二、实验难点
1.将进程加载到内存
1 | int elf_load_seg(Elf32_Phdr *ph, const void *bin, elf_mapper_t map_page, void *data) { |
理解elf_load_seg函数即可了解操作系统如何将进程写入内存。值得注意的是该函数是单页加载的。该函数将此过程分为三个部分执行:
- 如果
va并不是页对齐的,则加载到至多下一页的起始地址。如果是页对齐则不需要第一步过程。 - 以页为单位将进程加载入内存。
- 如果该段在文件中的内容的大小达不到为填入这段内容新分配的页面大小(即
bin_size<sg_size),那么余下的部分用 0 来填充。
在实现回调函数load_icode_mapper时,需要注意在加载时地址以页操作,将页使用page2kva函数转化成内核虚拟地址再进行操作。
2.时钟中断的过程
- 发生异常
- 处理器进入异常分发程序(本试验中是
exc_gen_entry函数,位于kern/entry.S),从异常向量组exception_handlers,定位对应异常处理函数。 - 中断处理程序
handle_int判断Cause寄存器是不是对应的 4 号中断位引发的中断,如果是,则执行
中断服务函数timer_irq。 - 中断服务函数
timer_irq响应时钟中断,并进入调度程序schedule调度新进程。 - 调度程序
schedule将时间片用尽的进程换为新进程,调用进程切换函数env_run使其运行。 - 进程切换函数
env_run将原进程上下文保存,将新进程寄存器状态写入cpu中,实现进程切换。
三、心得体会
本次实验主要学习了进程创建、加载、调度,理解CP0寄存器构成以及中断异常的处理过程,学习内容丰富,收获很多。