mit 6.S081 Lab4 traps

本次实验是让我们探寻xv6如何实现trap,需要阅读xv6 book的第四章

RISC-V assembly

占坑…这个我没做…

Backtrace

实验大意

这个实验让做一个在调用sys_sleep之前输出调用栈信息的小程序。

过程

在hint里面提供了一个极为抽象的图,不过很有用。

这张图给出了xv6中栈帧的结构,hints里面提供了一个函数返回fp,fp是栈顶的地址,fp-8是返回地址,fp-16是存放指向上一个栈帧的fp的地址,这样我们所需要的元素都集齐了,我们获得一个fp之后,打印我们需要的信息,然后再找到指向上一个栈帧的fp的地址,解引用这个fp就可以获得一个栈帧的栈顶地址了,所以只需要在kernel/riscv.h添加r_fp函数以及在kernel/printf.c中添加backtrace即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// kernel/printf.c
void backtrace()
{Alarm
uint64 fp = r_fp();
uint64 hight, low;
hight = PGROUNDUP(fp);//我们需要找到当前页的最高地址和最低地址,用来判断找栈帧的过程是否结束了。
low = PGROUNDDOWN(fp);
printf("backtrace:\n");
while(fp >= low && fp < hight)
{
//uint64 *fp_pointer = (uint64 *)fp;
printf("%p\n", *(uint64 *)(fp - 8));
fp = (*(uint64 *)(fp - 16));//记得获取的是fp这个指针指向的地址,而不是fp本身的值。
}

}

最后需要在kernel/sysproc.c的sys_sleep中调用backtrace

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// kernel/sysproc.c
uint64
sys_sleep(void)
{
backtrace(); //调用backtrace
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;
}
sleep(&ticks, &tickslock);
}
release(&tickslock);
return 0;
}

Alarm

这个实验我大概已经做完一个多月了…

实验大意

大概意思是让实现一个time trap,当时间到的时候,调用回调函数。
首先要在proc结构体中加入已用时间,和alarm传入的触发时间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
struct proc {
struct spinlock lock;

// p->lock must be held when using these:
enum procstate state; // Process state
struct proc *parent; // Parent process
void *chan; // If non-zero, sleeping on chan
int killed; // If non-zero, have been killed
int xstate; // Exit status to be returned to parent's wait
int pid; // Process ID

// these are private to the process, so p->lock need not be held.
uint64 kstack; // Virtual address of kernel stack
uint64 sz; // Size of process memory (bytes)
pagetable_t pagetable; // User page table
struct trapframe *trapframe; // data page for trampoline.S
struct trapframe backup_trapframe;
struct context context; // swtch() here to run process
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory
char name[16]; // Process name (debugging)

int siga_ticks;//zaizheli
int ticks_sum;
uint64 siga_handler;

int ishandling;
};


然后去sysproc.c中完成alarm对应的系统函数

1
2
3
4
5
6
7
8
9
10
11
12
uint64 sys_sigalarm(void)
{
int siga_ticks;//间隔多少时间片调用一次回调函数
uint64 siga_handler;//指向回调函数的指针
if(argint(0, &siga_ticks) < 0 || argaddr(1, &siga_handler) < 0)
return -1;
struct proc *p = myproc();
p->siga_handler = siga_handler;
p->siga_ticks = siga_ticks;
return 0;

}

然后还需要去syscall.c中添加声明,这里就略过了,和之前操作是一样的。
这样我们就完成了alarm的调用,以及进程信息中关于alarm的信息初始化。
之后我们就需要去trap中添加相应的代码,本来是想将trap调用的过程写一遍的,先留坑把…

1
2
3
4
5
6
7
8
9
// kernel/trap.c usertrap
if(which_dev == 2)//判断时候因为alarm中断的
{
if(p->siga_ticks != 0)
{
p->ticks_sum ++;//加一个时间片
}
yield();//因为是每个时间片cpu中的进程都需要调度,所以yield
}

当这个proc又被cpu执行的时候,会调用usertrapret函数,进行返回到用户态的准备,这里我们需要判断time是否到了,添加了一个ishandling标志位,标志是否这个进程时候就是在正在运行回调函数,如果是,那么就不要再次调用回调函数了。使用w_sepc将回调函数的地址赋值给pc,这样返回用户态的时候就会到达回调函数。backup_trapframe保存了当前的trapframe,用作回调函数结束后,返回原来调用前状态的备份。

1
2
3
4
5
6
7
8
// kernel/trap.c usertrapret
if(p->siga_ticks != 0 && p->ishandling != 1&&p->ticks_sum == p->siga_ticks)
{
p->ticks_sum = 0;
p->backup_trapframe = *(p->trapframe);
p->ishandling = 1;
w_sepc(p->siga_handler);
}

这次写的比较乱…因为隔了很久很久了,最近在也准备实习之类的事情…

作者

xiaomaotou31

发布于

2021-10-25

更新于

2021-11-14

许可协议

评论