BUAA_OS_lab3
BUAA_OS_lab3学习日记
Thinking思考题
Thinking 3.1
问:请结合 MOS 中的页目录自映射应用解释代码中 e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_V 的含义。
答:
User VPT中存放了1024个页表,由于自映射的关系,这个页表里又包括了页目录的页表,每一个页表中又有1024个页表项,UVPT:用户页表的起始处的内核虚拟地址
PDX(UVPT):UVPT所处的页目录号(即 UVPT 处于第 PDX(UVPT) 个页目录项所映射的4MB空间。PDX获得的是这个虚拟地址的页目录号。
e->env_pgdir:进程 e 的页目录的内核虚拟地址
PADDR(e->env_pgdir):进程 e 的页目录的物理地址
PADDR(e->env_pgdir) | PTE_V:页目录的物理基地址,加上权限位(只读权限)
自映射的意义即是只需要知道二级页表的初始地址,就能构造出页目录的初始地址以及指向页目录自身的PDE,这样,方便我们访问管理页表与页目录。
Thinking 3.2
问:elf_load_seg 以函数指针的形式,接受外部自定义的回调函数 map_page。 请你找到与之相关的 data 这一参数在此处的来源,并思考它的作用。没有这个参数可不可 以?为什么?
1 |
|
data 是传入的进程控制块指针,在 load_icode_mapper 和 load_icode 函数中被调用,在 load_icode 函数中 data 被赋予进程控制块的指针 e。
作用:在增加虚拟地址到物理地址映射的时候提供env_pgdir和env_asid;如果没有data,load_icode_mapper就不能知道当前进程空间的页目录基地址和asid,所以必须要有这个参数。
Thinking 3.3
问:结合 elf_load_seg 的参数和实现,考虑该函数需要处理哪些页面加载的情 况。
1 |
|
- 函数判断va是否页对齐,如果不对齐,需要将多余的地址记为offset,我们将最开头不对齐的部分 “剪切” 下来,先映射到内存的页中。
- 将段内所有页映射到物理空间
- 最后我们处理段大小大于数据大小的情况。在这一部分,我们不断创建新的页,但是并不向其中加载任何内容。
Thinking 3.4
问:这里的 env_tf.cp0_epc 存储的是物理地址还是虚拟地址?
根据上学期计组P7的知识,因为epc存储的是发生错误时CPU所处的指令地址,对于CPU来说,在程序中引用访存的地址都是虚拟地址,因此env_tf.cp0_epc毫无疑问存储的是虚拟地址.
Thinking 3.5
问:试找出 0、1、2、3 号异常处理函数的具体实现位置。8 号异常(系统调用) 涉及的 do_syscall() 函数将在 Lab4 中实现。
备注: C 的拓展语法 [first … last] = value 来对数组某个区间上的 元素赋成同一个值
查找结果如下:
kern/genex.S:20:NESTED(handle_int, TF_SIZE, zero)
kern/genex.S:29:END(handle_int)
handle_int在genex.S文件中
handle_mod和handle_tlb,handle_sys藏得比较深,三者都是通过genex.S文件中的宏函数BUILD_HANDLER实现的,代码如下:
1 |
|
Thinking 3.6
问:阅读 entry.S、genex.S 和 env_asm.S 这几个文件,并尝试说出时钟中断 在哪些时候开启,在哪些时候关闭。
entry.S
1 |
|
genex.S
1 |
|
当中断屏蔽位位为1且,CP0_CAUSE为时钟中断,且CP0_STATUS正常相应,体现为这几个寄存器储存的值做与运算的结果不为0,时钟中断开始,调度完成后结束。
Thinking 3.7
问:阅读相关代码,思考操作系统是怎么根据时钟中断切换进程的。
再调度函数中,如果满足切换条件(yield || count <= 0 || e == NULL || e->env_status != ENV_RUNNABLE
),如果当前进程没有就绪,将其移到调度链表尾部,反之则不用,接着选中调度链表头部进程达到调度目的。
Exercise
Exercise 3.1
实现进程控制快的初始化功能,具体实现参考注释即可。
1 |
|
Exercise 3.2
段地址的初始化使用 map_segment 函数在该页表中将内核数组 pages 和 envs 映射到了用户空间的 UPAGES 和 UENVS 处
map_segment功能是在一级页表基地址 pgdir 对应的两级页表结构中做段地址映射,将虚拟地 址段 [va,va+size) 映射到物理地址段 [pa,pa+size)
1 |
|
Exercise 3.3
env_setup_vm函数:初始化新进程的地址空间
步骤如下:
- 申请一块页表,并将页表转化为kernel地址赋值给e->env_pgdir
- 制了
UTOP
到UVPT
的虚拟地址空间对应的页表项 - 我们将
UVPT
虚拟地址映射到页目录本身的物理地址,并设置只读权限。这样的话,页目录中的项所对应的,就不只是二级页表,还包含有一个一级页表,也就是页目录自身。这就是自映射。
Exercise 3.4
步骤如下:
- 获取从空进程块表中获取到一个进程块
- 初始化新进程的地址空间
- 将进程块的各属性赋值(env_user_tlb_mod_entry、env_runs、env_tf.cp0_status……),手工初始化进程控制块。
- 将该进程块从空进程块表移除
Exercise 3.5
load_icode_mapper函数
需要分配所需的物理页面,并在页表中建立映射。若 src 非空,你还需要将该处的 ELF 数据拷贝到物理页面中
1 |
|
Exercise 3.6
步骤:
- elf_from做类型转化并检查是否为正确的ELF文件头
- 接下来使用ELF_FOREACH_PHDR_OFF遍历所有程序表头,在循环中,取出对应的程序头,如果其中的
p_type
类型为PT_LOAD
,说明其对应的程序需要被加载到内存中。我们调用elf_load_seg
函数来进行加载 - 将进程控制块中 trap frame 的 epc cp0 寄存器的值设置为 ELF 文件中设定的程序入口地址
Exercise 3.7
直接创建进程
1 |
|
Exercise 3.8
- 保存当前进程的上下文信息。
寄存器保存的地方是KSTACKTOP以下的一个sizeof(TrapFrame)大小的区域中,达到保存上下文的目的
- 切换 curenv 为即将运行的进程。
- 设置全局变量 cur_pgdir 为当前进程页目录地址,在 TLB 重填时将用到该全局变量。
- 调用 env_pop_tf 函数,恢复现场、异常返回。
Exercise 3.9
完成异常分发代码
1 |
|
Exercise 3.10
.text.exc_gen_entry 段和 .text.tlb_miss_entry 段需要被链接器放到特定的位置。
CPU 发生异常(除了用户态地址的 TLB Miss 异常)后,就会自动跳转 到地址 0x80000180 处;发生用户态地址的 TLB Miss 异常时,会自动跳转到地址 0x80000000 处。开始执行。
Exercise 3.11
时钟中断
将CP0_COUNT和CP0_COMPARE赋予适当的值
RESET_KCLOCK 宏将 Count 寄存器清零并将 Compare 寄存器配置为我们所期望的计时器周 期数,这就对 Timer 完成了配置。
1 |
|
Exercise 3.12
进程调度——时间片轮转的算法
无需进行切换时,我们只需要将剩余时间片长度 count 减去 1,然后调用 env_run 函数,继 续运行当前进程 curenv。在发生切换的情况下,我们还需要判断当前进程是否仍然就绪,如果 是则将其移动到调度链表的尾部。之后,我们选中调度链表首部的进程来调度运行,将剩余时间 片长度设置为其优先级。
难点分析
进程初始化与分配
初始化:
map_segment 函数 $\rightarrow$ env_init 函数
分配
env_setup_vm函数:初始化新进程的地址空间 $\rightarrow$ env_alloc函数
加载二进制镜像
1 |
|
进程创建与切换运行
创建:分配一个新的 Env 结构体,设置进程控制块,并将程序 载入到该进程的地址空间
进程切换的时候,为了保证下一次进入这个进程 的时候我们不会再“从头来过”,而是有记忆地从离开的地方继续往后走,我们要保存一些信息, 那么,需要保存什么信息呢?事实上,我们只需要保存进程的上下文信息,包括通用寄存器、HI、 LO 和 CP0 中的 Status,EPC,Cause 和 BadVAddr 寄存器。进程控制块除了 env_tf 其他的字 段在进程切换后还保留在原本的进程控制块中,并不会改变,因此不需要保存。(发生进程调度,或当陷入内 核时,会将当时的进程上下文环境保存在 env_tf 变量中(Trapframe结构体))
中断与异常
时间片:比如设定某个进程的时间片的长度为 200 倍的 TIMER_INTERVAL(时钟中断间隔),那么当 MOS 记录到该进程的执行中发生了 200 个时钟中断 时,MOS 就知晓该进程的时间片结束了。
最后留下一段有意思的空间分配
1 |
|
感想
本次作业围绕进程的创建、初始化、分配、映射、运行、切换等展开,难度有所提示,但大部分的操作和页表部分类似。