MIT 6.S081 实验4 笔记与心得

本文档详细介绍了RISC-V架构下的陷阱处理,包括系统调用、回溯和警报功能的实现。实验涉及理解汇编语言、函数调用参数存储、堆栈帧布局以及中断处理。在回溯功能中,通过堆栈帧指针行走来打印函数调用列表;在警报功能中,实现了周期性调用用户定义的函数并恢复中断前的状态。实验还讨论了如何处理和恢复寄存器,以及避免重复调用报警处理程序。通过一系列测试验证了这些功能的正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

lab 4:Traps


前期准备

本实验探讨了如何使用陷阱实现系统调用。你将首先做一个堆栈的热身练习,然后你将实现一个用户级陷阱处理的例子。

在你开始编码之前,请阅读xv6书的第4章,以及相关的源文件。

  • kernel/trampoline.S:从用户空间到内核空间再到用户空间的转换过程中所涉及的汇编。
  • kernel/trap. c: 处理所有中断的代码

RISC-V assembly

题目翻译

了解一点RISC-V的汇编是很重要的,你在6.004中接触过它。在你的xv6 repo中,有一个文件user/call.c。make fs.img编译了它,也产生了user/call.asm中程序的可读汇编版本。

阅读call.asm中函数g、f和main的代码。RISC-V的指导手册在参考页上。这里有一些你应该回答的问题(将答案存储在文件answer-traps.txt中)。

  1. 哪些寄存器包含函数的参数?例如,在main对printf的调用中,哪个寄存器包含13?->通过查阅汇编代码即可

  2. 在main的汇编代码中,对函数f的调用在哪里?对g的调用在哪里?(提示:编译器可以内联函数。)没有调用函数f和g,使用了内联函数

  3. 函数printf的地址是什么?通过查看代码和汇编即可

  4. 在主程序中调用printf的jalr之后,寄存器ra中的值是多少?

    这里需要注意jalr的指令含义:

    jalr 1544(ra) -> t = pc+4 , pc = sext(1544)+x(ra),pc = pc&~1 ,x(ra) = t

    和普通的jalr不同点在于这里没有目标寄存器rd

    jalr rd offset(rs) -> t = pc+4 =, pc = sext(offset)+x(rs),pc = pc&~1 ,x(rd) = t

    这里ra里保存的是下一次指令的地址pc+4

  5. 运行以下代码。

    unsigned int i = 0x00646c72printf("H%x Wo%s", 57616, &i)

    输出的结果是什么?

    通过输入上述代码,可得输出:HE110 World

    [这是一个ASCII表] ,将字节映射到字符。这个输出取决于RISC-V是小编码的这一事实。如果RISC-V是大面值的,你会把i设置为什么,以产生相同的输出?你是否需要把57616改成一个不同的值?

i= 0x726c6400,不需要改变57616,因为二进制数据和字符串读入的格式不同

在下面的代码中,'y='后面要打印什么?(注意:答案不是一个具体的值。)为什么会出现这种情况?
printf("x=%d y=%d", 3)。
主要考察C语言的不定参数的保存,参考risk-v的资料,可看出是a2寄存器进行保存第二个参数。那么a0保存什么了呢,思考一下哈!有问题私我。

在文档里需要主要的几点:

SRLI是逻辑右移(零被移到上方位);SRAI是算术右移(原来的符号位被复制到空出的上方位)。

题目答案
  1  r(a2) = 13;r(a1) = 12; a0 stores the RA          
  2  inline optimization
  3  0x628
  4  ra = pc+4 = 0x38
  5  HE110 World  change i -> 0x726c6400 only
  6  a1 store  first arg; a2 store second arg

BackTrace(回溯)

题目翻译

对于调试来说,有一个回溯通常是很有用的:在错误发生的点之上的堆栈中的函数调用的列表。

在kernel/printf.c中实现一个backtrace()函数,在sys_sleep中插入一个对该函数的调用,然后运行 ,调用sys_sleep。你的输出应该是这样的:bttest

backtrace:
0x0000000080002cda
0x0000000080002bb6
0x0000000080002898

在bttest之后退出qemu。在你的终端:地址可能略有不同,但如果你运行addr2line -e kernel/kernel(或riscv64-unknown-elf-addr2line -e kernel/kernel)并剪切和粘贴上述地址如下。你应该看到像这样的东西。

 $ addr2line -e kernel/kernel
    0x0000000080002de2
    0x0000000080002f4a
    0x0000000080002bfc
    Ctrl-D
  
    kernel/sysproc.c:74
    kernel/syscall.c:224
    kernel/trap.c:85

编译器在每个堆栈帧中放入一个帧指针,该指针持有调用者的帧指针地址。你的回溯应该使用这些帧指针在堆栈上行走,并在每个堆栈帧中打印出保存的返回地址。

一些小提示:

    <
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值