思考题

Thinking 3.1 请结合 MOS 中的页目录自映射应用解释代码中 e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_V 的含义。

一个页目录项映射一个含有 1K 个页表项的 4KB 页表,每一个页表映射一个 4MB 大小的虚拟地址空间。由于页目录映射的 4MB 空间就是二级页表结构所在的 4MB 空间,而用户二级页表结构存储在虚拟地址空间中的 UVPT 至 ULIM 处的 4MB 空间 ,因此页目录相当于第 PDX(UVPT)(取高 10 位)个页表,也就位于第 PDX(UVPT) 个页目录项,其物理地址 PADDR(e->env_pgdir) 存储在这个页目录项中。

Thinking 3.2 elf_load_seg 以函数指针的形式,接受外部自定义的回调函数 map_page。请你找到与之相关的 data 这一参数在此处的来源,并思考它的作用。没有这个参数可不可以?为什么?

int elf_load_seg(Elf32_Phdr *ph, const void *bin, elf_mapper_t map_page, void *data);load_icode 函数中被调用,调用语句为

1
2
3
4
5
6
// kern/env.c
static void load_icode(struct Env *e, const void *binary, size_t size) {
//...
panic_on(elf_load_seg(ph, binary + ph->p_offset, load_icode_mapper, e));
//...
}

可见此处 data 来源就是进程控制块 struct Env *e。回调函数 load_icode_mapper 中将 void *data 重新转换为 struct Env * 类型并作为进程控制块使用。

作用:用于将进程控制块指针传递给回调 load_icode_mapper,使得回调函数能够将程序的某一页映射到该进程的虚拟地址空间中。

不可以没有,否则无法找到该进程的页表。

Thinking 3.3 结合 elf_load_seg 的参数和实现,考虑该函数需要处理哪些页面加载的情况。

  • 首先是段的初始地址 va 按是否页对齐分为两种情况:

    未命名绘图.drawio (1)

  • 其次是段的实际大小 bin_size 与其在内存中占用的大小 sgsize 分为两种情况:

    bin_size = sgsize,则将程序段加载到 vava + bin_size 即可;

    bin_size < sgsize,则将程序段加载到 vava + bin_size 后,还要加载空内容 NULLva + bin_sizeva + sgsize

Thinking 3.4 思考上面这一段话,并根据自己在 Lab2 中的理解,回答: 你认为这里的 env_tf.cp0_epc 存储的是物理地址还是虚拟地址?

虚拟地址。

Thinking 3.5 试找出 0、1、2、3 号异常处理函数的具体实现位置。8 号异常(系统调用) 涉及的 do_syscall()函数将在 Lab4 中实现。

0 号异常处理函数 handle_intkern/genex.S 中:

1
2
3
4
5
6
7
8
9
10
NESTED(handle_int, TF_SIZE, zero)
mfc0 t0, CP0_CAUSE
mfc0 t2, CP0_STATUS
and t0, t2
andi t1, t0, STATUS_IM7
bnez t1, timer_irq
timer_irq:
li a0, 0
j schedule
END(handle_int)

1 号异常处理函数 handle_mod、2 和 3 号异常处理函数 handle_tlb 也在 kern/genex.S 中,不过用了宏进行了包装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.macro BUILD_HANDLER exception handler
NESTED(handle_\exception, TF_SIZE + 8, zero)
move a0, sp
addiu sp, sp, -8
jal \handler
addiu sp, sp, 8
j ret_from_exception
END(handle_\exception)
.endm

BUILD_HANDLER tlb do_tlb_refill
#...
BUILD_HANDLER mod do_tlb_mod
#...

Thinking 3.6 阅读 entry.S、genex.S 和 env_asm.S 这几个文件,并尝试说出时钟中断在哪些时候开启,在哪些时候关闭。

时钟中断在进入异常、中断处理时关闭,在退出异常、中断处理后开启。

Thinking 3.7 阅读相关代码,思考操作系统是怎么根据时钟中断切换进程的。

操作系统给每个进程分配一定大小的时间片,其大小依据时钟中断来衡量。每次时钟中断发生时,内核检测是否满足进程切换条件,若满足则切换进程,否则在下个时钟周期继续执行该进程。循环往复,直到进程切换。

难点分析

  1. 进程创建流程

    进程创建流程.drawio


  2. 时钟中断流程

    时钟中断流程.drawio

  3. UVPT 处的用户进程页表,以及位于 kseg0 中的 e->env_pgdir 页目录,理解它们的关系。

实验体会

本次实验主要涉及进程管理的初始化、进程的创建与切换,以及异常中断处理。

体会创建进程的流程,理解创建进程时对进程页目录的操作。