【MIT 6.S081】Lab4: traps

本文介绍了RISC-V架构中系统调用的实现过程,包括用户态通过ecall指令进入内核态,trapframe的使用,页表切换以及uservec和kernelvec的处理。此外,还涉及到了栈回溯(backtrace)功能的实现以及alarm机制,包括sigalarm和sigreturn系统调用的细节,如何在CPU时钟中断后恢复中断前的代码执行。


本Lab主要熟悉在trap中系统调用的实现。
笔者用时约4h

概述

首先讲讲我对系统调用时trap的理解,用户程序通过系统调用进入内核态,在汇编代码中的体现就是ecall指令,会把系统调用参数保存在寄存器a7中。
在用户态中执行ecall指令之后,会将pc寄存器的内容复制到sepc中,并将模式转换为监督者模式,且会将stvec复制到pc中
由于stvec的值为函数uservec(定义在kernel/trampoline.S:37)的地址,故在执行完ecall指令之后,会跳转到uservec函数中。由于ecall指令并没有切换页表的功能,于是在用户页表中必须要维护uservec函数虚拟地址的映射,这也就是trampoline页的功能啦。
uservec函数中,首先需要将用户态中的所有寄存器内容保存到该用户进程的trapframe中,一开始trapframe的首地址保存在sscratch寄存器中,首先代码将a0寄存器与sscratch寄存器进行交换,然后开始保存所有其他寄存器的值(根据提前规定好的偏移量,定义在kernel/proc.h:44)。然后需要把当前进程trapframe中的一些字段保存到对应的寄存器中,比如kernel_sp保存到sp寄存器之类的。最后需要将该内核页表地址写入satp寄存器中,切换页表为内核页表。这里再一次体现了,内核页表和进程页表共同映射了trampoline页的原因。
uservec函数最后调用了usertrap函数(定义在kernel/trap.c:37中),在该函数中,首先向stvec寄存器写入kernelvec函数(定义在kernel/kernelvec.S中)的地址(因为这个时候在内核态,于是trap由kernelvec函数进行处理),并将当前sepc寄存器中的值保存到trapframe的epc字段中,然后将epc加4,即跳过了ecall指令,指向用户态的下一条指令。然后调用syscall函数进行系统调用的操作。
syscall函数返回时,会调用usertrapret函数(定义在kernel/trap.c:103中),该函数将uservec重新写入stvec寄存器中,将trapframe中的一些字段设置好,并将模式设置为用户模式,以便处理下一次的用户态trap。最后,将trapframe的地址作为第一个参数(保存在a0中),将用户进程跟页表地址作为第二个参数(保存在a1中),调用userret函数(定义在kernel/trampoline.S:88中)。
在函数userret中,首先切换页表为用户进程页表,且将trapframe页的地址保存到寄存器a0中,然后恢复所有寄存器的值,将原始a0寄存器的值(在系统调用情况下,trapframe中的a0字段被设置为系统调用返回值)放在sscratch寄存器中,最后交换a0寄存器和sscratch寄存器的值,并使用sret指令(将模式变为用户模式,将sepc寄存器中的内容复制到pc寄存器中)回到用户态。

RISC-V assembly

回答一些问题

  1. Q: 哪些寄存器保存函数的参数?例如,在main对printf的调用中,哪个寄存器保存13?
    A: 寄存器a0-a7保存函数参数,比如printf的调用中,第一个参数是字符串地址保存在a0中,f(8)+1保存在a1中,13保存在a2中。
  2. Q: main的汇编代码中对函数f的调用在哪里?对g的调用在哪里(提示:编译器可能会将函数内联)
    A: 被编译器优化掉了,减少了函数的调用,从li a1, 12可以看出
  3. Q: printf函数位于哪个地址?
    A: 0x630, 在call.asm中搜一下就可以看到
  4. Q: 在main中printf的jalr之后的寄存器ra中有什么值?
    A: 在main中,printf的jalr指令会将下一条指令的地址(也就是0x38)保存在寄存器ra中,于是jalr之后的寄存器ra中的值为0x38
  5. Q: 运行以下代码。
    unsigned int i = 0x00646c72;
    printf("H%x Wo%s", 57616, &i);
    
    程序的输出是什么?
    A: HE110 World
    Q: 输出取决于RISC-V小端存储的事实。如果RISC-V是大端存储,为了得到相同的输出,你会把i设置成什么?是否需要将57616更改为其他值?
    如果是大端存储,那么将i设置为0x726c6400即可,不需要将57616改为其他值
  6. Q: 在下面的代码中,“y=”之后将打印什么(注:答案不是一个特定的值)?为什么会发生这种情况?
    printf("x=%d y=%d", 3);
    
    A: y将打印成什么取决于上一次a2寄存器中的内容

Backtrace

这一部分需要实现一个函数backtrace,在调用处打印当前栈中所有栈帧保存的返回地址信息。
那么首先需要获取当前栈帧的帧尾指针fp,根据文档提示,我们可以在文件kernel/riscv.h中添加一个函数进行获取。

// read the frame pointer
static inline uint64
r_fp()
{
   
   
  uint64 x;
  asm volatile(
MIT 6.S081 是麻省理工学院开设的一门关于操作系统的课程,课程全称为 **MIT 6.S081: Operating System Engineering**。该课程以实践为导向,通过让学生从零开始逐步构建一个简单的类 Unix 操作系统内核(基于 RISC-V 架构),深入理解操作系统的核心原理和实现机制。 课程内容涵盖线程、调度、虚拟内存、文件系统、系统调用、中断、设备驱动、同步机制等操作系统关键主题。课程材料公开,包括讲义、实验(labs)、视频讲座和源代码,非常适合自学。 ### 课程资源获取方式 1. **官方网站** MIT OpenCourseWare 提供了完整的课程材料,包括实验指导、源代码、讲义和部分视频讲座。 - 官网地址:[https://pdos.csail.mit.edu/6.828/2020/index.html](https://pdos.csail.mit.edu/6.828/2020/index.html) (注意:6.S0816.828 的新编号,内容基本一致) 2. **XV6 操作系统源码** 课程使用 xv6 作为教学操作系统,它是 Unix V6 的简化版,使用 C 语言编写,适合教学和学习。 - 源码地址:[https://github.com/mit-pdos/xv6-riscv](https://github.com/mit-pdos/xv6-riscv) 3. **实验(Labs)** 课程实验包括: - **Lab 1: Xv6 and Unix utilities** - **Lab 2: System calls** - **Lab 3: Page tables** - **Lab 4: Traps** - **Lab 5: Threads** - **Lab 6: Locking** - **Lab 7: File system** - **Project: Networking (optional)** 每个实验都要求修改 xv6 源码以实现特定功能,例如添加系统调用、实现线程调度、实现文件系统操作等。 4. **社区与讨论区** - **GitHub 项目**:许多学生将他们的实验成果上传到 GitHub,可以参考他们的实现思路和代码。 - **Reddit 和 Stack Overflow**:在 r/mit、r/operatingsystems 和 Stack Overflow 上可以找到相关讨论和问题解答。 - **知乎、优快云 等中文社区**:一些中文用户也分享了他们对 MIT 6.S081 的学习笔记和实验解析。 5. **学习建议** - 熟悉 C 语言和汇编基础。 - 掌握基本的数据结构(如链表、队列、树等)。 - 理解计算机体系结构(特别是 RISC-V)。 - 有操作系统基础理论知识(如进程、内存管理、文件系统等)。 ### 示例代码:添加系统调用 以下是一个简单的系统调用示例,用于在 xv6 中添加一个新的系统调用 `sys_helloworld`: ```c // user/user.h int helloworld(void); // user/usys.pl entry("helloworld"); // kernel/syscall.h #define SYS_helloworld 22 // kernel/syscall.c extern uint64 sys_helloworld(void); // kernel/sysproc.c uint64 sys_helloworld(void) { printf("Hello, world from kernel!\n"); return 0; } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值