实验内容网址:https://xv6.dgs.zone/labs/requirements/lab4.html
本实验的代码分支:https://gitee.com/dragonlalala/xv6-labs-2020/tree/traps2/
Backtrace
关键点:trapframe、栈
思路:
这道题的关键是栈结构,先阅读xv6中关于栈的知识(https://mit-public-courses-cn-translatio.gitbook.io/mit6-s081/lec05-calling-conventions-and-stack-frames-risc-v/5.5-stack)。
![[图片]](https://i-blog.csdnimg.cn/blog_migrate/4d80369e7f78a6937fb61acb814d2a62.png)
阅读链接的资料,我们可以知道,
- 每调用一个函数,函数都会为自己创建一个Stack Frame。
- 栈是从高地址开始向低地址使用。所以栈总是向下增长。
- Return address总是会出现在Stack Frame的第一位,指向前一个Stack Frame的指针也会出现在栈中的固定位置
- 第一个寄存器是SP(Stack Pointer),SP始终指向栈顶(当前可用内存的最低地址),随着压栈/弹栈实时更新。而FP仅标记当前栈帧的起始位置,可能位于栈的较高地址(如函数调用链的固定位置)。FP-8位是返回地址,FP-16位是上一个stack frame的fp地址。
- 保存前一个Stack Frame的指针的原因是为了让我们能跳转回去。
XV6在内核中以页面对齐的地址为每个栈分配一个页面。你可以通过PGROUNDDOWN(fp)和PGROUNDUP(fp)(参见kernel/riscv.h)来计算栈页面的顶部和底部地址。
步骤&代码:
- 将下面的函数添加到kernel/riscv.h
static inline uint64
r_fp()
{
uint64 x;
asm volatile("mv %0, s0" : "=r" (x) );
return x;
}
- 在kernel/defs.h中添加backtrace的原型,在printf.c中定义backtrace函数。
void
backtrace(void){
// 读取当前Frame Pointer
uint64 fp = r_fp();
while(PGROUNDUP(fp) - PGROUNDDOWN(fp) == PGSIZE){
// 返回地址保存在-8偏移的位置
uint64 ret_addr = *(uint64*)(fp-8);
printf("%p\n",ret_addr);
// 前一个帧指针保存在-16偏移的位置
fp = *(uint64*)(fp-16);
}
}
- 在sys_sleep中添加对backtrace函数的调用。
uint64
sys_sleep(void)
{
int n;
uint ticks0;
if(argint(0, &n) < 0)
return -1;
acquire(&tickslock);
ticks0 = ticks;
while(ticks - ticks0 < n){
if(myproc()->killed){
release(&tickslock);
return -1;
}

本文介绍了在RISC-V架构的实验中,如何处理trapframe和栈结构,包括函数调用时栈帧的创建、返回地址的存储,以及在用户空间中实现定时器触发的报警处理,涉及syscall扩展和trapframe备份等技术。
最低0.47元/天 解锁文章
2775






