Skip to content

操作系统 - lab5 Demand Paging

Abstract

一些踩过的坑,以及一个 kernel 执行流程总结

基本信息

时间:2023 秋冬

实验文档:

kernel 执行流程

kernel 执行流程
- opensbi 执行完毕
- _start: 完成 stvec, sie, mtimecmp, sstatus 和栈的设置,然后依次调用以下函数
    - setup_vm: 填写页表
    - relocate: 用设置好的页表修改 satp, 启动虚拟内存
    - mm_init: 完成内存分配函数的初始化
    - setup_vm_final: 切换到新的页表
    - task_init: 初始化进程
        - 对于 idle 以外的线程,在 task_struct 中添加 stack 和 segment-01(1)对应的两段 vma
- start_kernel: 不进入 test 等时钟中断,而是直接调用 schedule 调度走
- schedule: 根据 policy 选择下一个要调度的线程,调用 switch_to 至该线程
- switch_to: 获取先后线程的 PCB 地址,调用__switch_to
- __switch_to: 当前上下文存入 PCB,加载下一个进程的 PCB
- __dummy: 切用户栈,sret 返回用户段,而 sepc 是我们初始化的时候就指定的的 ENTRY
- ENTRY: PC 跳转至 ENTRY,但是此时该页的映射并未完成,PAGE FAULT
- _traps: PAGE FAULT,保存上下文进入 trap_handler
- trap_handler: 确认是 PAGE FAULT 后,调用 do_page_fault
- do_page_fault: 根据 current 的 vma 确认是段错误、文件页缺页还是匿名页缺页,分配新页,填写内容(2),填写页表,返回
- _traps: 恢复上下文返回,此后对应页已有映射
- ENTRY: 恢复执行,此后调度或者遇到新的缺页均同上处理
  1. 也即 readelf 时得到的 segment-01,包括.text .rodata .bss
  2. 清零或者拷贝文件内容

栈切换问题

文档中指出,由用户发起的中断,我们需要在_traps开头切换到内核栈,但是对于内核发起的中断就不需要。而要判断中断是否由内核发起就要判断sscratch是否为 0,在 lab4 中我把 t0 压栈后用 t0 来检验。

但是在 lab5 中这一方法行不通,因为当我们试图把 t0 压栈的时候我们的栈可能根本就是没分配的用户栈,那么这个压栈的行为只会引发新的 PAGE FAULT,如此永远循环。

为了寻找解决方法可以先试图去查看 linux 时如何解决这一问题的,这是对应的代码

可以看到 linux 使用了 tp(thread pointer)以及指令csrwr,前者倒不是必要的使用,我们主要关注到csrwr指令可以实现 csr 与其他寄存器的交换,从而我们 sp 与sscratch的切换就不需要其他寄存器了,这就是正确的解决方式。

vmas

在 kernel 中,我们分配了一整页,其中的低地址放了个 task_struct,高地址就是栈顶(如下图,来自实验文档)。所以我们要稍微限制一下 vmas 的大小,不能把分配的一页全给用了,还要留点给栈用。

                    ┌─────────────┐◄─── High Address
                    │             │
                    │    stack    │
                    │             │
                    │             │
              sp ──►├──────┬──────┤
                    │      │      │
                    │      ▼      │
                    │             │
                    │             │
                    │             │
                    │             │
    4KB Page        │             │
                    │             │
                    │             │
                    │             │
                    ├─────────────┤
                    │             │
                    │             │
                    │ task_struct │
                    │             │
                    │             │
                    └─────────────┘◄─── Low Address

Comments