MIT 6.S081 操作系统 LAB4:Traps
Lab: traps
RISC-V assembly
有关RISC-V的汇编,分析call.c中的代码
call.c:
int g(int x) {
return x+3;
}
int f(int x) {
return g(x);
}
void main(void) {
printf("%d %d\n", f(8)+1, 13);
exit(0);
}
call.asm:
int g(int x) {
0: 1141 addi sp,sp,-16
2: e422 sd s0,8(sp)
4: 0800 addi s0,sp,16
return x+3;
}
6: 250d addiw a0,a0,3
8: 6422 ld s0,8(sp)
a: 0141 addi sp,sp,16
c: 8082 ret
000000000000000e <f>:
int f(int x) {
e: 1141 addi sp,sp,-16
10: e422 sd s0,8(sp)
12: 0800 addi s0,sp,16
return g(x);
}
14: 250d addiw a0,a0,3
16: 6422 ld s0,8(sp)
18: 0141 addi sp,sp,16
1a: 8082 ret
000000000000001c <main>:
void main(void) {
1c: 1141 addi sp,sp,-16
1e: e406 sd ra,8(sp)
20: e022 sd s0,0(sp)
22: 0800 addi s0,sp,16
printf("%d %d\n", f(8)+1, 13);
24: 4635 li a2,13
26: 45b1 li a1,12
28: 00000517 auipc a0,0x0
2c: 7c050513 addi a0,a0,1984 # 7e8 <malloc+0xea>
30: 00000097 auipc ra,0x0
34: 610080e7 jalr 1552(ra) # 640 <printf>
exit(0);
38: 4501 li a0,0
3a: 00000097 auipc ra,0x0
3e: 27e080e7 jalr 638(ra) # 2b8 <exit>
Which registers contain arguments to functions? For example, which register holds 13 in main's call to printf?
a0保存返回值,a1保存参数12,a2保存参数13
Where is the call to function f in the assembly code for main? Where is the call to g? (Hint: the compiler may inline functions.)
没有具体的调用,编译器优化成了内联函数,直接产生函数调用的结果
At what address is the function printf located?
0x640
30: 00000097 auipc ra,0x0
将pc的值放入ra,此时ra的值为0x30
34: 610080e7 jalr 1552(ra) # 640 <printf>
相对寻址,1552+48=1600=0x640
What value is in the register ra just after the jalr to printf in main?
ra保存返回地址,故是调用printf
后的下一条指令地址:0x38
Run the following code. What is the output? Here's an ASCII table that maps bytes to characters.
unsigned int i = 0x00646c72;
printf("H%x Wo%s", 57616, &i);
RISC-V是小端,低地址保存低位数据
57616=0xe110
在ASCII中,72对应r,6c对应l,64对应d
故输出为He110 World
如果RISC-V是大端,则i的值应为0x726c6400
In the following code, what is going to be printed after 'y='? (note: the answer is not a specific value.) Why does this happen?
printf("x=%d y=%d", 3);
y的输出是未定义的,取决于定义寄存器的值
Backtrace
For debugging it is often useful to have a backtrace: a list of the function calls on the stack above the point at which the error occurred.
取出fp栈帧指针,不断往前读取,因为xv6分配栈是按页对齐的,所以读到页对齐循环结束
栈帧布局如下:
fp-8的地址为return address,fp-16为上一个栈帧的地址
void backtrace()
{
printf("backtrace:\n");
uint64 fp=r_fp();
while(PGROUNDDOWN(fp)!=fp)
{
uint64 addr=*((uint64*)(fp-8));
printf("%p\n",addr);
fp=*((uint64*)(fp-16));
}
}
Alarm
实现一个定时器,每隔一定数量的cpu时钟,执行handler函数
修改proc结构体
//alarm
int interval;
void (*fn) ();//handler
int count;
int handling;//表示是否正在执行handler函数
struct trapframe save_info;//保存信息
系统调用
sigalarm
设置定时器
uint64
sys_sigalarm(void)
{
struct proc *p = myproc();
int interval;
void(*fn)();
if(argint(0, &interval) < 0)
return -1;
if(argaddr(1, (uint64*)&fn) < 0)
return -1;
p->interval=interval;
p->fn=fn;
p->count=interval;
return 0;
}
sigreturn
将保存下来的trapframe还原回去,还原寄存器现场
uint64
sys_sigreturn(void)
{
struct proc *p = myproc();
p->handling=0;
*(p->trapframe)=p->save_info;
return 0;
}
处理时钟中断
每个时钟中断,进行相应的处理
// give up the CPU if this is a timer interrupt.
if(which_dev == 2)
{
if(p->interval!=0&&!p->handling)
{
if(--p->count==0)
{
p->count=p->interval;
p->handling=1;
p->save_info=*(p->trapframe);
p->trapframe->epc=(uint64)p->fn;
}
}
yield();
}
思路有以下:
- 通过判断interval是否==0决定是否设置了定时器
- 判断
p->handling
,如果正在执行handler
则不改变闹钟时间 - 进入
handler
前要保存当前trapframe,方便在sigreturn
中还原 - 改变pc的值为
handler
的地址,执行处理函数