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寄存器构成以及中断异常的处理过程,学习内容丰富,收获很多。