本文记录了 Lab: traps 的实验过程

Lab: traps

这个 lab 是研究使用 traps 实现系统调用。

  • kernel/trampoline.S:涉及将用户空间切换到内核空间并返回用户空间的汇编代码
  • kernel/trap.c:处理中断的代码
1
2
3
git fetch
git checkout traps
make clean

RISC-V assembly (easy)

了解 RISC-V 的汇编代码,然后回答几个问题

  1. 寄存器 a1 和 a2 包含了 printf 的参数,13 在寄存器 a2 中
  2. main 函数中没有调用 f,g,它们被内联了
  3. printf 函数位于 ra = 0x30 + 1536(0x600) = 0x630
  4. 0x38
  5. %x 表示以十六进制形式输出,%s 以字符形式输出,所以结果为:He110 World。如果 RISC-V 是大端模式的话,那么 i 应为 0x00726c64 才能保证和上面输出一样;不需要改变 57616 这个值。
  6. y= 后面会输出一个随机数

Backtrace (moderate)

实验要求:实现一个类似 GDB 的 backtrace() 函数,它会列出堆栈上调用函数的列表。

按提示的步骤,首先读取寄存器 s0 保存的 fp 指针,return adressfp - 8 处,Prev fpfp -16

实验步骤

  1. kernel/defs.h 中声明函数原型,在 sys_sleep 中调用 backtrace()
1
2
3
4
5
6
7
8
9
10
11
12
// kernel/defs.h
void printfinit(void);
void backtrace(void); // add for lab traps

// kernel/sysproc.c
sys_sleep(void)
{
int n;
uint ticks0;
backtrace();
// ...
}
  1. kernel/riscv.h 中实现读取寄存器 s0 的函数
1
2
3
4
5
6
7
static inline uint64
r_fp()
{
uint64 x;
asm volatile("mv %0, s0" : "=r" (x) );
return x;
}
  1. kernel/printf.c 中实现 backtrace()
1
2
3
4
5
6
7
8
9
10
void
backtrace()
{
printf("backtrace:\n");
uint64 fp = r_fp();
while(PGROUNDDOWN(fp) < PGROUNDUP(fp)){
printf("%p\n", *((uint64*)(fp - 8)));
fp = *(uint64*)(fp - 16);
}
}

注意:

fp 保存了 frame pointer 的地址,需要通过 (uint64*) 将它转成无符号长整型指针,再通过 * 运算符取得那个地址的值。

测试

1
2
make qemu
bttest

退出 qemu 后,在终端输入 bttest 打印q出来的地址

1
2
3
4
addr2line -e kernel/kernel
0x0000000080002de2
0x0000000080002f4a
0x0000000080002bfc

Alarm (hard)

要求:实现一个功能,在用户进程每运行 n 个时钟周期后,调用该进程提供的 handler 函数

实验思路

  • alarmtest 添加到 Makefile 文件中编译,添加两个系统调用:int sigalarm(int n, void (*handler)())void sigreturn(void)(Lab:System Call 已经介绍了实现系统调用的步骤)。然后在进程结构体的定义中添加几个字段:ticks,handler,count
  • 在 test0 中,我们先实现 sigalarm,这个函数的作用是初始化 struct proc 中新增的字段。然后在 usertrap 中的时钟中断的那个判断语句中添加代码进行处理,让 p->trapframe->epc 保存 handler 函数的地址,以便返回用户空间时,可以执行 handler 函数。此时运行 alarmtest 能通过 test0。
  • 在 test1/test2中,由于要使用 sigreturn 这个系统调用,当 handler 处理完成后,会返回到用户程序被时钟中断的那个地方,因此需要保存用户的寄存器。在 struct proc 添加一个字段 struct trapframe traps,每当要执行 handler 函数时,它会保存 p->trapframe 中的内容,在 sigreturn 中,又恢复 p->trapframe 的值。

实验步骤

  • 这里略过在各文件对系统调用的定义

  • kernel/proc.h 中添加下面字段

1
2
3
4
int ticks; // alarm interval
int count; // how many ticks have passed since the last call
uint64 handler; // the pointer to the handler function
struct trapframe traps;
  • kernel/trap.c 的处理时钟中断的判断逻辑中,添加相应代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void
usertrap(void)
{
// ...
// give up the CPU if this is a timer interrupt.
if(which_dev == 2){
if(++p->count == p->ticks){ // need call handler function
// save p->trapframe to p->traps
memmove(&(p->traps), p->trapframe, sizeof(struct trapframe));
p->trapframe->epc = p->handler;
}
yield();
}
// ...
}
  • kernel/sysproc.c 中添加两个系统调用的实现
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
// sys_sigalarm() should store the alarm interval 
// and the pointer to the handler function
int
sys_sigalarm(void)
{
struct proc *p = myproc();
int ticks;
uint64 handler;

if(argint(0, &ticks) < 0)
return -1;
if(argaddr(1, &handler) < 0)
return -1;

p->ticks = ticks;
p->handler = handler;

return 0;
}
int
sys_sigreturn()
{
struct proc *p = myproc();
p->count = 0; // reset p->count
// restore p->trapframe from p->traps
memmove(p->trapframe, &(p->traps), sizeof(struct trapframe));
return 0;
}

测试

1
2
make qemu
alarmtest
1
make grade

Diff

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
diff --git a/Makefile b/Makefile
index 1fa367e..a74296b 100644
--- a/Makefile
+++ b/Makefile
@@ -175,6 +175,7 @@ UPROGS=\
$U/_grind\
$U/_wc\
$U/_zombie\
+ $U/_alarmtest\



diff --git a/kernel/defs.h b/kernel/defs.h
index 4b9bbc0..9148b4c 100644
--- a/kernel/defs.h
+++ b/kernel/defs.h
@@ -80,6 +80,7 @@ int pipewrite(struct pipe*, uint64, int);
void printf(char*, ...);
void panic(char*) __attribute__((noreturn));
void printfinit(void);
+void backtrace(void); // add for lab traps

// proc.c
int cpuid(void);
diff --git a/kernel/printf.c b/kernel/printf.c
index e1347de..057faba 100644
--- a/kernel/printf.c
+++ b/kernel/printf.c
@@ -121,6 +121,7 @@ panic(char *s)
printf("panic: ");
printf(s);
printf("\n");
+ backtrace();
panicked = 1; // freeze uart output from other CPUs
for(;;)
;
@@ -132,3 +133,13 @@ printfinit(void)
initlock(&pr.lock, "pr");
pr.locking = 1;
}
+void
+backtrace()
+{
+ printf("backtrace:\n");
+ uint64 fp = r_fp();
+ while(PGROUNDDOWN(fp) < PGROUNDUP(fp)){
+ printf("%p\n", *((uint64*)(fp - 8)));
+ fp = *(uint64*)(fp - 16);
+ }
+}
diff --git a/kernel/proc.h b/kernel/proc.h
index 9c16ea7..8f8ad3e 100644
--- a/kernel/proc.h
+++ b/kernel/proc.h
@@ -103,4 +103,9 @@ struct proc {
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory
char name[16]; // Process name (debugging)
+
+ int ticks; // alarm interval
+ int count; // how many ticks have passed since the last call
+ uint64 handler; // the pointer to the handler function
+ struct trapframe traps;
};
diff --git a/kernel/riscv.h b/kernel/riscv.h
index 0aec003..d193d59 100644
--- a/kernel/riscv.h
+++ b/kernel/riscv.h
@@ -352,3 +352,10 @@ sfence_vma()

typedef uint64 pte_t;
typedef uint64 *pagetable_t; // 512 PTEs
+static inline uint64
+r_fp()
+{
+ uint64 x;
+ asm volatile("mv %0, s0" : "=r" (x) );
+ return x;
+}
\ No newline at end of file
diff --git a/kernel/syscall.c b/kernel/syscall.c
index c1b3670..24bfccd 100644
--- a/kernel/syscall.c
+++ b/kernel/syscall.c
@@ -104,6 +104,8 @@ extern uint64 sys_unlink(void);
extern uint64 sys_wait(void);
extern uint64 sys_write(void);
extern uint64 sys_uptime(void);
+extern uint64 sys_sigalarm(void);
+extern uint64 sys_sigreturn(void);

static uint64 (*syscalls[])(void) = {
[SYS_fork] sys_fork,
@@ -127,6 +129,8 @@ static uint64 (*syscalls[])(void) = {
[SYS_link] sys_link,
[SYS_mkdir] sys_mkdir,
[SYS_close] sys_close,
+[SYS_sigalarm] sys_sigalarm,
+[SYS_sigreturn] sys_sigreturn,
};

void
diff --git a/kernel/syscall.h b/kernel/syscall.h
index bc5f356..67ca3a4 100644
--- a/kernel/syscall.h
+++ b/kernel/syscall.h
@@ -20,3 +20,5 @@
#define SYS_link 19
#define SYS_mkdir 20
#define SYS_close 21
+#define SYS_sigalarm 22
+#define SYS_sigreturn 23
diff --git a/kernel/sysproc.c b/kernel/sysproc.c
index e8bcda9..50d8cf4 100644
--- a/kernel/sysproc.c
+++ b/kernel/sysproc.c
@@ -57,6 +57,7 @@ sys_sleep(void)
{
int n;
uint ticks0;
+ backtrace();

if(argint(0, &n) < 0)
return -1;
@@ -95,3 +96,31 @@ sys_uptime(void)
release(&tickslock);
return xticks;
}
+// sys_sigalarm() should store the alarm interval
+// and the pointer to the handler function
+int
+sys_sigalarm(void)
+{
+ struct proc *p = myproc();
+ int ticks;
+ uint64 handler;
+
+ if(argint(0, &ticks) < 0)
+ return -1;
+ if(argaddr(1, &handler) < 0)
+ return -1;
+
+ p->ticks = ticks;
+ p->handler = handler;
+
+ return 0;
+}
+int
+sys_sigreturn()
+{
+ struct proc *p = myproc();
+ p->count = 0; // reset p->count
+ // restore p->trapframe from p->traps
+ memmove(p->trapframe, &(p->traps), sizeof(struct trapframe));
+ return 0;
+}
\ No newline at end of file
diff --git a/kernel/trap.c b/kernel/trap.c
index a63249e..f1d4022 100644
--- a/kernel/trap.c
+++ b/kernel/trap.c
@@ -77,8 +77,14 @@ usertrap(void)
exit(-1);

// give up the CPU if this is a timer interrupt.
- if(which_dev == 2)
+ if(which_dev == 2){
+ if(++p->count == p->ticks){ // need call handler function
+ // save p->trapframe to p->traps
+ memmove(&(p->traps), p->trapframe, sizeof(struct trapframe));
+ p->trapframe->epc = p->handler;
+ }
yield();
+ }

usertrapret();
}
diff --git a/user/user.h b/user/user.h
index b71ecda..57404e0 100644
--- a/user/user.h
+++ b/user/user.h
@@ -23,6 +23,8 @@ int getpid(void);
char* sbrk(int);
int sleep(int);
int uptime(void);
+int sigalarm(int ticks, void (*handler)());
+int sigreturn(void);

// ulib.c
int stat(const char*, struct stat*);
diff --git a/user/usys.pl b/user/usys.pl
index 01e426e..fa548b0 100755
--- a/user/usys.pl
+++ b/user/usys.pl
@@ -36,3 +36,5 @@ entry("getpid");
entry("sbrk");
entry("sleep");
entry("uptime");
+entry("sigalarm");
+entry("sigreturn");