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中)。
-
哪些寄存器包含函数的参数?例如,在main对printf的调用中,哪个寄存器包含13?->通过查阅汇编代码即可
-
在main的汇编代码中,对函数f的调用在哪里?对g的调用在哪里?(提示:编译器可以内联函数。)没有调用函数f和g,使用了内联函数
-
函数printf的地址是什么?通过查看代码和汇编即可
-
在主程序中调用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
-
运行以下代码。
unsigned int i = 0x00646c72。 printf("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
编译器在每个堆栈帧中放入一个帧指针,该指针持有调用者的帧指针地址。你的回溯应该使用这些帧指针在堆栈上行走,并在每个堆栈帧中打印出保存的返回地址。
一些小提示:
-
<