6.S081 Xv6 Lab 4 traps

Meaning Unknown's Head Image

Lab: traps

6.S081 的 Xv6 RISC-V Lab traps,实验内容:

1
2
3
$ git fetch
$ git checkout traps
$ make clean

RISC-V assembly

这题没什么具体要做的,就看一看,跟着题目学一下 RISC- V 汇编。

Backtrace

这题主要是实现打印函数栈。就是 GDB 里面 bt 的这种效果:

1
2
3
4
5
6
(gdb) bt
#0 fork () at kernel/proc.c:260
#1 0x0000000080002c3c in sys_fork () at kernel/sysproc.c:29
#2 0x0000000080002bb0 in syscall () at kernel/syscall.c:140
#3 0x000000008000289a in usertrap () at kernel/trap.c:67
#4 0x0000000000000050 in ?? ()

关键代码

kernel/riscv.h: 实现一个获取当前 frame pointer 的方法:

1
2
3
4
5
6
7
static inline uint64
r_fp()
{
uint64 x;
asm volatile("mv %0, s0" : "=r" (x) );
return x;
}

kernel/printf.c: 打印 backtrace:

1
2
3
4
5
6
7
8
9
10
void
backtrace(void)
{
printf("backtrace:\n");
uint64 fp = r_fp();
while (PGROUNDDOWN(fp) < PGROUNDUP(fp)) {
printf("%p\n", *(uint64*)(fp-8));
fp = *(uint64*)(fp-16);
}
}

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
diff --git a/kernel/defs.h b/kernel/defs.h
index 4b9bbc0..137c786 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);

// proc.c
int cpuid(void);
diff --git a/kernel/printf.c b/kernel/printf.c
index e1347de..fbdeb68 100644
--- a/kernel/printf.c
+++ b/kernel/printf.c
@@ -121,6 +121,9 @@ panic(char *s)
printf("panic: ");
printf(s);
printf("\n");
+
+ backtrace();
+
panicked = 1; // freeze uart output from other CPUs
for(;;)
;
@@ -132,3 +135,18 @@ printfinit(void)
initlock(&pr.lock, "pr");
pr.locking = 1;
}
+
+// print a backtrace:
+// a list of the function calls on the stack above
+// the point at which the error occurred.
+void
+backtrace(void)
+{
+ 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/riscv.h b/kernel/riscv.h
index 0aec003..c95316e 100644
--- a/kernel/riscv.h
+++ b/kernel/riscv.h
@@ -319,6 +319,16 @@ sfence_vma()
asm volatile("sfence.vma zero, zero");
}

+// compiler stores the frame pointer of the currently
+// executing function in s0.
+// this function reads current frame pointer, namely s0
+static inline uint64
+r_fp()
+{
+ uint64 x;
+ asm volatile("mv %0, s0" : "=r" (x) );
+ return x;
+}

#define PGSIZE 4096 // bytes per page
#define PGSHIFT 12 // bits of offset within a page
diff --git a/kernel/sysproc.c b/kernel/sysproc.c
index e8bcda9..a520959 100644
--- a/kernel/sysproc.c
+++ b/kernel/sysproc.c
@@ -58,6 +58,8 @@ sys_sleep(void)
int n;
uint ticks0;

+ backtrace();
+
if(argint(0, &n) < 0)
return -1;
acquire(&tickslock);

Alarm

做一套系统调用,在用户进程每运行指定次数时钟后,调用该进程提供的一个 handler。

其实也不难,(我觉得比页表简单),跟着 hint 一步步做就行了。

关键实现

  1. kernel/proc.h: 在 proc 结构体里添加需要的字段(间隔时长、已运行时长计数、处理函数地址(vm)、保存 alarm 前的寄存器):
1
2
3
4
5
6
7
struct proc {
...
int alarm_interval; // the alarm interval (ticks)
int alarm_passed; // how many ticks have passed since the last call
uint64 alarm_handler; // pointer to the alarm handler function
struct trapframe etpfm; // trapframe to resume
}
  1. 添加 sys_sigalarm 系统调用:接收参数,为进程设置 alarm 的间隔时长和处理函数。最终的实现在 kernel/sysproc.c (添加新系统调用的完整步骤见:6.S081 Xv6 Lab 2: system calls):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
uint64
sys_sigalarm(void)
{
int interval;
uint64 handler;

if(argint(0, &interval) < 0)
return -1;

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

struct proc *p = myproc();

p->alarm_interval = interval;
p->alarm_handler = handler;

return 0;
}
  1. usertrapkernel/trap.c)中处理时钟中断时,如果进程需要 alarm 就保存当前 trapframe 到 etpfm,调用 handler (把 handler 地址放到 trapframe->epc,回到用户空间之后就会运行该函数):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void
usertrap(void)
{
...
if(which_dev == 2) {
// alarm
if (p->alarm_interval) {
if (++p->alarm_passed == p->alarm_interval) {
memmove(&(p->etpfm), p->trapframe, sizeof(struct trapframe));
// return to alarm handler: call p->alarm_handler();
p->trapframe->epc = p->alarm_handler;
}
}
yield();
}
...
}
  1. 实现 sys_sigalarm 系统调用,恢复 alarm 前的 trapframe(回到用户空间就会接着 alarm 之前的 PC 开始运行),把 alarm_passed 计数器置为零(允许下一次 alarm):
1
2
3
4
5
6
7
8
uint64
sys_sigreturn(void)
{
struct proc *p = myproc();
memmove(p->trapframe, &(p->etpfm), sizeof(struct trapframe));
p->alarm_passed = 0;
return 0;
}

某bug的sizeof

我做的时候不小心写了个 sizeof 的语法错误,找了半个小时呢。。。(这个问题超初学者的,,我对8起 K&R)

我一开始是这么写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct trapframe {...}

struct proc {
...
struct trapframe *trapframe;
struct trapframe etpfm;
}

void
usertrap(void)
{
...
memmove(&(p->etpfm), p->trapframe, sizeof(p->trapframe));
...
}

uint64
sys_sigreturn(void)
{
...
memmove(p->trapframe, &(p->etpfm), sizeof(p->trapframe));
...
}

解决:把 sizeof(p->trapframe) 改成 sizeof(struct trapframe)

Diff

emmm,上一题做完忘记 commit 了,所以下面这个 diff 也包含了上一题(backtrace)的:

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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
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/grade-lab-traps b/grade-lab-traps
index 058e77b..8619bbe 100755
--- a/grade-lab-traps
+++ b/grade-lab-traps
@@ -60,7 +60,7 @@ def test_alarmtest_test2():
def test_usertests():
r.run_qemu(shell_script([
'usertests'
- ]), timeout=300)
+ ]), timeout=500)
r.match('^ALL TESTS PASSED$')

@test(1, "time")
diff --git a/kernel/defs.h b/kernel/defs.h
index 4b9bbc0..137c786 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);

// proc.c
int cpuid(void);
diff --git a/kernel/printf.c b/kernel/printf.c
index e1347de..fbdeb68 100644
--- a/kernel/printf.c
+++ b/kernel/printf.c
@@ -121,6 +121,9 @@ panic(char *s)
printf("panic: ");
printf(s);
printf("\n");
+
+ backtrace();
+
panicked = 1; // freeze uart output from other CPUs
for(;;)
;
@@ -132,3 +135,18 @@ printfinit(void)
initlock(&pr.lock, "pr");
pr.locking = 1;
}
+
+// print a backtrace:
+// a list of the function calls on the stack above
+// the point at which the error occurred.
+void
+backtrace(void)
+{
+ 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.c b/kernel/proc.c
index dab1e1d..237fecb 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -127,6 +127,11 @@ found:
p->context.ra = (uint64)forkret;
p->context.sp = p->kstack + PGSIZE;

+ // Initialize alarm
+ p->alarm_interval = 0;
+ p->alarm_passed = 0;
+ p->alarm_handler = 0;
+
return p;
}

diff --git a/kernel/proc.h b/kernel/proc.h
index 9c16ea7..2dac44f 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 alarm_interval; // the alarm interval (ticks)
+ int alarm_passed; // how many ticks have passed since the last call
+ uint64 alarm_handler; // pointer to the alarm handler function
+ struct trapframe etpfm; // trapframe to resume, 啊, 不会xv6的动态内存分配,所以这里直接 hardcode 一个对象了。
};
diff --git a/kernel/riscv.h b/kernel/riscv.h
index 0aec003..c95316e 100644
--- a/kernel/riscv.h
+++ b/kernel/riscv.h
@@ -319,6 +319,16 @@ sfence_vma()
asm volatile("sfence.vma zero, zero");
}

+// compiler stores the frame pointer of the currently
+// executing function in s0.
+// this function reads current frame pointer, namely s0
+static inline uint64
+r_fp()
+{
+ uint64 x;
+ asm volatile("mv %0, s0" : "=r" (x) );
+ return x;
+}

#define PGSIZE 4096 // bytes per page
#define PGSHIFT 12 // bits of offset within a page
diff --git a/kernel/syscall.c b/kernel/syscall.c
index c1b3670..eb079af 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..382d781 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..5b64016 100644
--- a/kernel/sysproc.c
+++ b/kernel/sysproc.c
@@ -58,6 +58,8 @@ sys_sleep(void)
int n;
uint ticks0;

+ backtrace();
+
if(argint(0, &n) < 0)
return -1;
acquire(&tickslock);
@@ -95,3 +97,32 @@ sys_uptime(void)
release(&tickslock);
return xticks;
}
+
+uint64
+sys_sigalarm(void)
+{
+ int interval;
+ uint64 handler;
+
+ if(argint(0, &interval) < 0)
+ return -1;
+
+ if(argaddr(1, &handler) < 0)
+ return -1;
+
+ struct proc *p = myproc();
+
+ p->alarm_interval = interval;
+ p->alarm_handler = handler;
+
+ return 0;
+}
+
+uint64
+sys_sigreturn(void)
+{
+ struct proc *p = myproc();
+ memmove(p->trapframe, &(p->etpfm), sizeof(struct trapframe));
+ p->alarm_passed = 0;
+ return 0;
+}
diff --git a/kernel/trap.c b/kernel/trap.c
index a63249e..9f2a64b 100644
--- a/kernel/trap.c
+++ b/kernel/trap.c
@@ -77,8 +77,20 @@ usertrap(void)
exit(-1);

// give up the CPU if this is a timer interrupt.
- if(which_dev == 2)
+ if(which_dev == 2) {
+ // alarm
+ if (p->alarm_interval /*&& p->alarm_handler*/) { // handler addr might be 0
+ if (++p->alarm_passed == p->alarm_interval) {
+ memmove(&(p->etpfm), p->trapframe, sizeof(struct trapframe));
+ // return to alarm handler: call p->alarm_handler();
+ p->trapframe->epc = p->alarm_handler;
+ // printf("[DEBUG] alarm: %s(%d), handler=%x\n",
+ // p->name, p->pid, p->alarm_handler);
+ // p->alarm_passed = 0; // sigreturn 时再恢复: prevent re-entrant calls to the handler
+ }
+ }
yield();
+ }

usertrapret();
}
diff --git a/user/alarmtest.c b/user/alarmtest.c
index 38f09ff..427c460 100644
--- a/user/alarmtest.c
+++ b/user/alarmtest.c
@@ -101,6 +101,7 @@ test1()
// restored correctly, causing i or j or the address ofj
// to get an incorrect value.
printf("\ntest1 failed: foo() executed fewer times than it was called\n");
+ printf("\ti=%d, j=%d\n", i, j);
} else {
printf("test1 passed\n");
}
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");

实验结果

最后,make grade (我的机器上跑 usertests 巨慢,要改一下 timeout 才能通过。。):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
== Test answers-traps.txt == answers-traps.txt: OK 
== Test backtrace test ==
$ make qemu-gdb
backtrace test: OK (4.7s)
== Test running alarmtest ==
$ make qemu-gdb
(3.9s)
== Test alarmtest: test0 ==
alarmtest: test0: OK
== Test alarmtest: test1 ==
alarmtest: test1: OK
== Test alarmtest: test2 ==
alarmtest: test2: OK
== Test usertests ==
$ make qemu-gdb
usertests: OK (448.3s)
(Old xv6.out.usertests failure log removed)
== Test time ==
time: OK
Score: 85/85

EOF


By CDFMLR 2020-03-28

顶部图片来自于小歪API,系随机选取的图片,仅用于检测屏幕显示的机械、光电性能,与文章的任何内容及观点无关,也并不代表本人局部或全部同意、支持或者反对其中的任何内容及观点。如有侵权,联系删除。