操作系统lab3

LAB3实验报告

思考题

Thingking 1

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

  • 将页目录自身物理地址存储到页目录中虚拟地址为UVPT的页目录项中,并将其权限位设置为只读,完成页目录自映射。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
o      KERNBASE -----> +----------------------------+----|-------0x8002 0000    |
o | Exception Entry | \|/ \|/
o ULIM -----> +----------------------------+------------0x8000 0000-------
o | User VPT | PDMAP /|\
o UVPT -----> +----------------------------+------------0x7fc0 0000 |
o | pages | PDMAP |
o UPAGES -----> +----------------------------+------------0x7f80 0000 |
o | envs | PDMAP |
o UTOP,UENVS -----> +----------------------------+------------0x7f40 0000 |
o UXSTACKTOP -/ | user exception stack | PTMAP |
o +----------------------------+------------0x7f3f f000 |
o | | PTMAP |
o USTACKTOP ----> +----------------------------+------------0x7f3f e000 |
o | normal user stack | PTMAP |
o +----------------------------+------------0x7f3f d000 |
a | | |
a ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
a . . |
a . . kuseg
a . . |
a |~~~~~~~~~~~~~~~~~~~~~~~~~~~~| |
a | | |
o UTEXT -----> +----------------------------+------------0x0040 0000 |
o | reserved for COW | PTMAP |
o UCOW -----> +----------------------------+------------0x003f f000 |
o | reversed for temporary | PTMAP |
o UTEMP -----> +----------------------------+------------0x003f e000 |
o | invalid memory | \|/
a 0 ------------> +----------------------------+ ----------------------------

由此虚拟内存空间结构图可知,UVPT即为用户页表起始位置,将其对应物理地址存入页目录中等比例偏移的位置即可完成页目录的自映射。

Thingking 2

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

  • data这一参数来源于load_icode函数中的进程控制块结构体指针e,作为回调函数map_page的传入参数,提供了此处回调函数的具体实现函数load_icode_mapper中所需要的env->env_pgdirenv->env_asid两个量。

  • 如果没有data这个参数将不能提供上述两个量,无法实现所需功能。

Thingking 3

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

  • 函数部分代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
u_long offset = va - ROUNDDOWN(va, PAGE_SIZE);
if (offset != 0) {
if ((r = map_page(data, va, offset, perm, bin,MIN(bin_size, PAGE_SIZE - offset))) != 0) {
return r;
}
}
for (i = offset ? MIN(bin_size, PAGE_SIZE - offset) : 0; i < bin_size; i += PAGE_SIZE) {
if ((r = map_page(data, va + i, 0, perm, bin + i, MIN(bin_size - i, PAGE_SIZE))) != 0) {
return r;
}
}
while (i < sgsize) {
if ((r = map_page(data, va + i, 0, perm, NULL, MIN(sgsize - i, PAGE_SIZE))) != 0) {
return r;
}
i += PAGE_SIZE;
}

由代码可知,需要处理一下几种情况:

  • 段虚拟地址与页虚拟地址的边界不对齐,则需要使用u_long offset = va - ROUNDDOWN(va, PAGE_SIZE)计算出其相对于页边界的偏移量

  • 处理了段大小小于页大小的情况

  • 如果二进制文件大小超过页大小,则需要将多个页进行映射

  • 如果二进制文件大小小于段大小,则需要继续进行映射,直到达到段的大小为止

Thingking 4

你认为这里的env_tf.cp0_epc存储的是物理地址还是虚拟地址?

  • 由前面的学习我们知道CPU只能通过虚拟地址进行访存,因此此处env_tf.cp0_epc储存的是虚拟地址

Thingking 5

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

  • handle_int位于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)
  • handle_modhandle_tlb位于genex.S中,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    .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

    此函数可实现根据expection调用对应的handler函数进行处理。

    继续阅读genex.S我们可以发现他实现了如下宏:

    1
    2
    BUILD_HANDLER tlb do_tlb_refill
    BUILD_HANDLER mod do_tlb_mod

    进一步可知handle_tlb会进一步调用do_tlb_refillhandle_mod会进一步调用do_tlb_mod函数进行处理。

Thingking 6

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

  • 阅读entry.S,有如下代码:
1
2
3
mfc0    t0, CP0_STATUS
and t0, t0, ~(STATUS_UM | STATUS_EXL | STATUS_IE)
mtc0 t0, CP0_STATUS

可知,当开始处理异常时关闭时钟中断。

env_asm.S中的如下代码可知,异常处理结束后调用schedule后再直接调用env_run后再调用env_pop_tf函数恢复时钟中断。

1
2
3
4
5
6
7
8
LEAF(env_pop_tf)
.set reorder
.set at
mtc0 a1, CP0_ENTRYHI
move sp, a0
RESET_KCLOCK
j ret_from_exception
END(env_pop_tf)

难点分析

  1. 理解回调函数map_page的使用:使用回调函数可以使调用的elf_load_seg函数能够根据回调函数和传入的参数执行不同的操作,增加了elf_load_seg函数的复用性。 ## 实验体会

使用链表宏能够大幅度降低代码的复杂度,本次实验中需要实现空闲进程链表,只需要将lab2中使用的链表宏的传入参数修改为空闲进程链表即可,十分方便,同时也避免了直接在代码中使用大段的链表创建代码,使代码更简洁易读。

原创说明

参考了往年学长的博客:https://b1fang.github.io/2023/04/11/OS-Lab3/