Lab syscall: System calls

tips

在上一个实验,我们学习了怎么使用系统调用来写用户程序,这次实验,我们来实现如何写一个系统调用。

中文版实验手册

一个简单的从用户到内核空间的调试方法¶
有同学可能问,能不能跳过汇编代码,直接打打断点到C代码呢?答案是可以的。下面还是以ls中的fstat系统调用为例,介绍一个较为简单的调试步骤:
Step1: 先在终端输入“make qemu-gdb”。
接着,按下F5, 或者 点击左侧按钮运行与调试,并点击左上角绿色三角(Attach to gdb)。
再点击“运行”,让xv6正常运行,直到出现“$”,表示已经进入shell中。
Step2: 在调试控制台,输入“interrupt”。
Step3: 在kernel/trap.c:128处打断点,继续点击“运行”。
Step4: 在xv6的shell中输入ls,以启动ls程序;程序停留在kernel/trap.c:128处。
Step5: 接下来,我们需要在调试窗口左下角删除原有的内核态断点,并通过调试控制台,加载ls的调试符号。在其中输入file user/_ls。
Step6: 打开user/ls.c文件,找到main()函数,在第78行打上断点。点击“运行”。
Step7: 在ls.c第38行打上断点,点击“运行”,让程序停留在执行fstat系统调用的地方。
Step8: 在调试控制台,输入file kernel/kernel,加载kernel的调试符号。
Step9: 在syscall.c文件中第138行syscall();打上断点,点击“运行”,让程序停留在syscall()函数。
Step10: 接下来,你就可以根据需要来调试你想要分析的代码吧。

小技巧:使用在指定文件命令查找给定的关键词

  • fish终端
    1
    2
    3
    grand@Lubuntu ~/xv6-labs-2020 (syscall)> grep fork (find kernel/ -name "*.h")
    kernel/defs.h:int fork(void);
    kernel/syscall.h:#define SYS_fork 1
  • bash终端
    1
    2
    3
    grand@Lubuntu ~/xv6-labs-2020 (syscall)> grep fork $(find kernel/ -name "*.h")
    kernel/defs.h:int fork(void);
    kernel/syscall.h:#define SYS_fork 1

trace

先来分析一下user/trace.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
#include "kernel/param.h"
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int
main(int argc, char *argv[])
{
int i;
char *nargv[MAXARG];

if(argc < 3 || (argv[1][0] < '0' || argv[1][0] > '9')){
fprintf(2, "Usage: %s mask command\n", argv[0]);
exit(1);
}

if (trace(atoi(argv[1])) < 0) { // 系统调用trace,设置mask值为argv[1]
fprintf(2, "%s: trace failed\n", argv[0]);
exit(1);
}

for(i = 2; i < argc && i < MAXARG; i++) { // 提取command
nargv[i-2] = argv[i];
}
exec(nargv[0], nargv); // exec command
exit(0);
}

如果执行trace 32 grep hello README,那么trace.c先执行系统调用trace(32)来设置当前程序的mask为32,再通过系统调用exec执行grep hello README

为了让gcc编译trace.c

在Makefile 的UPROGS的最后添加$U/_trace\

1
2
3
UPROGS=\
......
$U/_trace\

函数声明

我们发现在user/user.h中有系统调用的函数声明,所以我们也添加一个系统调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// system calls
int fork(void);
int exit(void) __attribute__((noreturn));
int wait(void);
int pipe(int*);
int write(int, void*, int);
int read(int, void*, int);
int close(int);
int kill(int);
int exec(char*, char**);
int open(char*, int);
int mknod(char*, short, short);
int unlink(char*);
int fstat(int fd, struct stat*);
int link(char*, char*);
int mkdir(char*);
int chdir(char*);
int dup(int);
int getpid(void);
char* sbrk(int);
int sleep(int);
int uptime(void);
int trace(int); // add a new system call trace

系统调用的过程

user/usys.pl是用来生成系统调用的汇编代码

1
2
3
4
5
6
7
8
sub entry {
my $name = shift;
print ".global $name\n";
print "${name}:\n";
print " li a7, SYS_${name}\n"; // 将系统调用号放入a7寄存器
print " ecall\n"; // 调用ecall,触发软中断,进入内核
print " ret\n";
}

这里perl语言自动生成汇编语言usys.S,是用户态系统调用接口,可以看到首先把系统调用号压入a7寄存器,然后就直接ecall进入系统内核。
而我们刚才syscall那个函数就把a7寄存器的数字读出来调用对应的函数,所以这里就是系统调用用户态和内核态的切换接口。

添加entry("trace");以系统调用函数trace为例,事实上是要调用时把SYS_trace(trace的系统调用编号)压入到寄存器a7当中,然后调用ecall进入kernel。

生成的usys.S如下

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
# generated by usys.pl - do not edit
#include "kernel/syscall.h"
.global fork
fork:
li a7, SYS_fork
ecall
ret
.global exit
exit:
li a7, SYS_exit
ecall
ret
.global wait
wait:
li a7, SYS_wait
ecall
ret
.global pipe
pipe:
li a7, SYS_pipe
ecall
ret
.global read
read:
li a7, SYS_read
ecall
ret
.global write
write:
li a7, SYS_write
ecall
ret
.global close
close:
li a7, SYS_close
ecall
ret
.global kill
kill:
li a7, SYS_kill
ecall
ret
.global exec
exec:
li a7, SYS_exec
ecall
ret
.global open
open:
li a7, SYS_open
ecall
ret
.global mknod
mknod:
li a7, SYS_mknod
ecall
ret
.global unlink
unlink:
li a7, SYS_unlink
ecall
ret
.global fstat
fstat:
li a7, SYS_fstat
ecall
ret
.global link
link:
li a7, SYS_link
ecall
ret
.global mkdir
mkdir:
li a7, SYS_mkdir
ecall
ret
.global chdir
chdir:
li a7, SYS_chdir
ecall
ret
.global dup
dup:
li a7, SYS_dup
ecall
ret
.global getpid
getpid:
li a7, SYS_getpid
ecall
ret
.global sbrk
sbrk:
li a7, SYS_sbrk
ecall
ret
.global sleep
sleep:
li a7, SYS_sleep
ecall
ret
.global uptime
uptime:
li a7, SYS_uptime
ecall
ret
.global trace
trace:
li a7, SYS_trace
ecall
ret

添加系统调用号

kernel/syscall.h

1
#define SYS_trace  22

实现系统调用

添加系统调用编号对应的系统调用函数名函数指针数组,添加内核态的系统函数调用声明,实现内核态系统调用

1.kernel/syscall.c,函数指针数组static uint64 (*syscalls[])(void)加上:

1
2
3
4
5
static uint64 (*syscalls[])(void) = {
...
[SYS_trace] sys_trace,
[SYS_sysinfo] sys_sysinfo,
};

该表的功能是:根据系统调用编号,找到并调用对应的函数


2.kernel/syscall.c,给内核态的系统调用sys_trace加上声明

1
extern uint64 sys_trace(void);


3.实现sys_trace

kernel/proc.h的struct proc中添加成员变量mask表示trace掩码

1
2
3
4
struct proc {
...
int mask; // using in trace
};

kernel/sysproc.c中,定义sys_trace内核函数

1
2
3
4
5
6
7
8
9
10
uint64 sys_trace(void)
{
int mask;
if (argint(0, &mask) < 0) // int trace(int)的形参保存在寄存器a0中,mask = a0
{
return -1;
}
myproc()->mask = mask; // 设置当前进程的mask
return 0;
}

argint这个函数调用了argraw函数,这个函数会去读寄存器a0到a5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static uint64
argraw(int n)
{
struct proc *p = myproc();
switch (n) {
case 0:
return p->trapframe->a0;
case 1:
return p->trapframe->a1;
case 2:
return p->trapframe->a2;
case 3:
return p->trapframe->a3;
case 4:
return p->trapframe->a4;
case 5:
return p->trapframe->a5;
}
panic("argraw");
return -1;
}

kernel/proc.c中,修改fork函数,使得在子进程继承父进程的mask

1
2
3
4
5
6
7
8
9
10
11

int
fork(void)
{
...
safestrcpy(np->name, p->name, sizeof(p->name));
pid = np->pid;
np->mask=p->mask; // 子进程继承父进程的mask
release(&np->lock);
...
}

kernel/syscall.c中,修改syscall函数,在系统调用返回后判断trace掩码并输出信息

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
static const char *const syscallsname[] =  // 系统调用号对应的系统名称
{
[SYS_fork] "fork",
[SYS_exit] "exit",
[SYS_wait] "wait",
[SYS_pipe] "pipe",
[SYS_read] "read",
[SYS_kill] "kill",
[SYS_exec] "exec",
[SYS_fstat] "fstat",
[SYS_chdir] "chdir",
[SYS_dup] "dup",
[SYS_getpid] "getpid",
[SYS_sbrk] "sbrk",
[SYS_sleep] "sleep",
[SYS_uptime] "uptime",
[SYS_open] "open",
[SYS_write] "write",
[SYS_mknod] "mknod",
[SYS_unlink] "unlink",
[SYS_link] "link",
[SYS_mkdir] "mkdir",
[SYS_close] "close",
[SYS_trace] "trace",
[SYS_sysinfo] "sysinfo"
};

void
syscall(void)
{
if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
p->trapframe->a0 = syscalls[num]();
if (p->mask>>num & 1) { // 如果当前的系统调用是trace掩码对应的系统调用,输出信息。
printf("%d: syscall %s -> %d\n", p->pid, syscallsname[num], p->trapframe->a0);
}
} else {
printf("%d %s: unknown sys call %d\n",
p->pid, p->name, num);
p->trapframe->a0 = -1;
}
}

Sysinfo

分析

1
2
3
4
5
6
7
grand@Lubuntu ~/xv6-labs-2020 (syscall)> grep sysinfo (find kernel/ -name "*.h")
kernel/sysinfo.h:struct sysinfo {
grand@Lubuntu ~/xv6-labs-2020 (syscall)> cat kernel/sysinfo.h
struct sysinfo {
uint64 freemem; // amount of free memory (bytes)
uint64 nproc; // number of process
};

从这里,我们可以知道struct sysinfo用来记录系统信息,即空闲内存和进程数)

1
2
struct proc proc[NPROC];
enum procstate { UNUSED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };

这个数组就保存着所有的进程,所以只要遍历这个数组判断状态就好了,
判断状态就是在proc结构体中的enum procstate state;

查看sysinfotest.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
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
#include "kernel/types.h"
#include "kernel/riscv.h"
#include "kernel/sysinfo.h"
#include "user/user.h"


void
sinfo(struct sysinfo *info) {
if (sysinfo(info) < 0) { // call the syscall "sysinfo", get the system infomation
printf("FAIL: sysinfo failed");
exit(1);
}
}

//
// use sbrk() to count how many free physical memory pages there are.
//
int
countfree()
{
uint64 sz0 = (uint64)sbrk(0);
struct sysinfo info;
int n = 0;

while(1){
if((uint64)sbrk(PGSIZE) == 0xffffffffffffffff){
break;
}
n += PGSIZE;
}
sinfo(&info);
if (info.freemem != 0) {
printf("FAIL: there is no free mem, but sysinfo.freemem=%d\n",
info.freemem);
exit(1);
}
sbrk(-((uint64)sbrk(0) - sz0));
return n;
}

void
testmem() {
struct sysinfo info;
uint64 n = countfree();

sinfo(&info);

if (info.freemem!= n) {
printf("FAIL: free mem %d (bytes) instead of %d\n", info.freemem, n);
exit(1);
}

if((uint64)sbrk(PGSIZE) == 0xffffffffffffffff){
printf("sbrk failed");
exit(1);
}

sinfo(&info);

if (info.freemem != n-PGSIZE) {
printf("FAIL: free mem %d (bytes) instead of %d\n", n-PGSIZE, info.freemem);
exit(1);
}

if((uint64)sbrk(-PGSIZE) == 0xffffffffffffffff){
printf("sbrk failed");
exit(1);
}

sinfo(&info);

if (info.freemem != n) {
printf("FAIL: free mem %d (bytes) instead of %d\n", n, info.freemem);
exit(1);
}
}

void
testcall() {
struct sysinfo info;

if (sysinfo(&info) < 0) {
printf("FAIL: sysinfo failed\n");
exit(1);
}

if (sysinfo((struct sysinfo *) 0xeaeb0b5b00002f5e) != 0xffffffffffffffff) {
printf("FAIL: sysinfo succeeded with bad argument\n");
exit(1);
}
}

void testproc() {
struct sysinfo info;
uint64 nproc;
int status;
int pid;

sinfo(&info);
nproc = info.nproc;

pid = fork();
if(pid < 0){
printf("sysinfotest: fork failed\n");
exit(1);
}
if(pid == 0){
sinfo(&info);
if(info.nproc != nproc+1) {
printf("sysinfotest: FAIL nproc is %d instead of %d\n", info.nproc, nproc+1);
exit(1);
}
exit(0);
}
wait(&status);
sinfo(&info);
if(info.nproc != nproc) {
printf("sysinfotest: FAIL nproc is %d instead of %d\n", info.nproc, nproc);
exit(1);
}
}

int
main(int argc, char *argv[])
{
printf("sysinfotest: start\n");
testcall();
testmem();
testproc();
printf("sysinfotest: OK\n");
exit(0);
}

添加声明

  • MakefileUPROGS的最后添加$U/_sysinfo\
  • user/user.h中的系统调用函数声明int sysinfo(struct sysinfo*);和结构体声明struct sysinfo
  • user/usys.pl中添加entry("sysinfo");
  • kernel/syscall.h添加系统函数编号#define SYS_sysinfo 23
  • kernel/syscall.c添加内核态函数声明,如下所示
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    extern uint64 sys_sysinfo(void);
    static uint64 (*syscalls[])(void) = {
    ...
    [SYS_sysinfo] sys_sysinfo,
    };

    static const char *const syscallsname[] =
    {
    ...
    [SYS_sysinfo] "sysinfo",
    };

实现内核函数

kernel/kalloc.c添加get_freemem函数的定义,获取当前空闲内存字节数

1
2
3
4
5
6
7
8
9
10
11
12
13
uint64 get_freemem(void)
{
acquire(&kmem.lock);
struct run *r = kmem.freelist;
uint64 cnt = 0;
while(r)
{
cnt++;
r = r->next;
}
release(&kmem.lock);
return cnt * PGSIZE;
}

kernel/proc.c中添加函数get_nproc的定义,获取当前空闲进程数量

1
2
3
4
5
6
7
8
9
10
11
12
13
uint64 get_nproc()
{
struct proc *p;
uint64 cnt = 0;
for (p = proc; p < &proc[NPROC]; p++)
{
if (p->state != UNUSED)
{
cnt++;
}
}
return cnt;
}

kernel/defs.h添加函数get_freemem与get_nproc声明

1
2
3
4
// kalloc.c
uint64 get_freemem(void);
// proc.c
uint64 get_nproc();

kernel/sysproc.c添加sys_sysinfo内核函数定义,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "sysinfo.h"
uint64 sys_sysinfo(void)
{
uint64 addr;
if (argaddr(0, &addr) < 0)
{
return -1;
}
struct sysinfo info;
struct proc *p=myproc();
info.freemem=get_freemem();
info.nproc=get_nproc();

if(copyout(p->pagetable, addr, (char*)&info, sizeof(info))) {
return -1;
}
return 0;
}

参考 kernel/sysfile.c/sys_fstat 和 kernel/file.c/filestat 使用 copyput() 将内核数据传输到用户态。
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
把在内核地址src开始的len大小的数据拷贝到用户进程pagetable的虚地址dstva处,
所以sysinfo实现里先用argaddr读进来我们要保存的在用户态的数据sysinfo的指针地址,
然后再把从内核里得到的sysinfo开始的内容以sizeof(sysinfo)大小的的数据复制到这个指针上,
其实就是同一个数据结构,所以这样直接复制过去就可以了。

评分

1
2
3
4
5
6
7
8
9
10
grand@Lubuntu ~/xv6-labs-2020 (syscall) [1]> python3 ./grade-lab-syscall
make: 'kernel/kernel' is up to date.
== Test trace 32 grep == trace 32 grep: OK (2.7s)
== Test trace all grep == trace all grep: OK (1.9s)
== Test trace nothing == trace nothing: OK (2.0s)
== Test trace children == trace children: OK (18.5s)
== Test sysinfotest == sysinfotest: OK (5.3s)
== Test time ==
time: OK
Score: 35/35

对比git修改内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
grand@Lubuntu ~/xv6-labs-2020 (syscall)> git diff origin/syscall syscall --stat
Makefile | 2 ++
kernel/defs.h | 2 ++
kernel/kalloc.c | 16 ++++++++++++++++
kernel/proc.c | 14 ++++++++++++++
kernel/proc.h | 1 +
kernel/syscall.c | 35 +++++++++++++++++++++++++++++++++++
kernel/syscall.h | 2 ++
kernel/sysproc.c | 33 +++++++++++++++++++++++++++++++++
time.txt | 1 +
user/trace.c | 8 ++++----
user/user.h | 3 +++
user/usys.pl | 2 ++
12 files changed, 115 insertions(+), 4 deletions(-)
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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
diff --git a/Makefile b/Makefile
index f0beb51..15ab2e8 100644
--- a/Makefile
+++ b/Makefile
@@ -149,6 +149,8 @@ UPROGS=\
$U/_grind\
$U/_wc\
$U/_zombie\
+ $U/_trace\
+ $U/_sysinfotest\



diff --git a/kernel/defs.h b/kernel/defs.h
index 4b9bbc0..8c6e633 100644
--- a/kernel/defs.h
+++ b/kernel/defs.h
@@ -63,6 +63,7 @@ void ramdiskrw(struct buf*);
void* kalloc(void);
void kfree(void *);
void kinit(void);
+uint64 get_freemem(void);

// log.c
void initlog(int, struct superblock*);
@@ -104,6 +105,7 @@ void yield(void);
int either_copyout(int user_dst, uint64 dst, void *src, uint64 len);
int either_copyin(void *dst, int user_src, uint64 src, uint64 len);
void procdump(void);
+uint64 get_nproc(void);

// swtch.S
void swtch(struct context*, struct context*);
diff --git a/kernel/kalloc.c b/kernel/kalloc.c
index fa6a0ac..079331c 100644
--- a/kernel/kalloc.c
+++ b/kernel/kalloc.c
@@ -80,3 +80,19 @@ kalloc(void)
memset((char*)r, 5, PGSIZE); // fill with junk
return (void*)r;
}
+
+// return the size of free memory
+uint64
+get_freemem(void)
+{
+ acquire(&kmem.lock);
+ struct run *r = kmem.freelist;
+ uint64 cnt = 0;
+ while(r)
+ {
+ cnt++;
+ r = r->next;
+ }
+ release(&kmem.lock);
+ return cnt * PGSIZE;
+}
diff --git a/kernel/proc.c b/kernel/proc.c
index 6afafa1..8377978 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -294,6 +294,9 @@ fork(void)
pid = np->pid;

np->state = RUNNABLE;
+
+ // copy the trace mask from the parent to the child process
+ np->mask = p->mask;

release(&np->lock);

@@ -693,3 +696,14 @@ procdump(void)
printf("\n");
}
}
+
+// return the number of unused processes
+uint64
+get_nproc(void)
+{
+ struct proc *p;
+ uint64 cnt = 0;
+ for (p = proc; p < &proc[NPROC]; p++)
+ if (p->state != UNUSED) cnt++;
+ return cnt;
+}
diff --git a/kernel/proc.h b/kernel/proc.h
index 9c16ea7..4e582b4 100644
--- a/kernel/proc.h
+++ b/kernel/proc.h
@@ -103,4 +103,5 @@ struct proc {
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory
char name[16]; // Process name (debugging)
+ int mask; // whose bits specify which system calls to trace
};
diff --git a/kernel/syscall.c b/kernel/syscall.c
index c1b3670..8854960 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_trace(void);
+extern uint64 sys_sysinfo(void);

static uint64 (*syscalls[])(void) = {
[SYS_fork] sys_fork,
@@ -127,6 +129,34 @@ static uint64 (*syscalls[])(void) = {
[SYS_link] sys_link,
[SYS_mkdir] sys_mkdir,
[SYS_close] sys_close,
+[SYS_trace] sys_trace,
+[SYS_sysinfo] sys_sysinfo,
+};
+
+static const char *const syscallsname[] = {
+[SYS_fork] "fork",
+[SYS_exit] "exit",
+[SYS_wait] "wait",
+[SYS_pipe] "pipe",
+[SYS_read] "read",
+[SYS_kill] "kill",
+[SYS_exec] "exec",
+[SYS_fstat] "fstat",
+[SYS_chdir] "chdir",
+[SYS_dup] "dup",
+[SYS_getpid] "getpid",
+[SYS_sbrk] "sbrk",
+[SYS_sleep] "sleep",
+[SYS_uptime] "uptime",
+[SYS_open] "open",
+[SYS_write] "write",
+[SYS_mknod] "mknod",
+[SYS_unlink] "unlink",
+[SYS_link] "link",
+[SYS_mkdir] "mkdir",
+[SYS_close] "close",
+[SYS_trace] "trace",
+[SYS_sysinfo] "sysinfo",
};

void
@@ -138,6 +168,11 @@ syscall(void)
num = p->trapframe->a7;
if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
p->trapframe->a0 = syscalls[num]();
+ // print the trace output
+ if (p->mask >> num & 1) {
+ printf("%d: syscall %s -> %d\n",
+ p->pid, syscallsname[num], p->trapframe->a0);
+ }
} else {
printf("%d %s: unknown sys call %d\n",
p->pid, p->name, num);
diff --git a/kernel/syscall.h b/kernel/syscall.h
index bc5f356..0dfedc7 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_trace 22
+#define SYS_sysinfo 23
diff --git a/kernel/sysproc.c b/kernel/sysproc.c
index e8bcda9..f6bc9ac 100644
--- a/kernel/sysproc.c
+++ b/kernel/sysproc.c
@@ -6,6 +6,7 @@
#include "memlayout.h"
#include "spinlock.h"
#include "proc.h"
+#include "sysinfo.h"

uint64
sys_exit(void)
@@ -95,3 +96,35 @@ sys_uptime(void)
release(&tickslock);
return xticks;
}
+
+uint64
+sys_trace(void)
+{
+ int mask;
+ if (argint(0, &mask) < 0)
+ {
+ return -1;
+ }
+ myproc()->mask = mask;
+ return 0;
+}
+
+uint64
+sys_sysinfo(void)
+{
+ uint64 addr;
+ if (argaddr(0, &addr) < 0)
+ {
+ return -1;
+ }
+ struct sysinfo info;
+ struct proc *p = myproc();
+ info.freemem = get_freemem();
+ info.nproc = get_nproc();
+
+ if(copyout(p->pagetable, addr, (char*)&info, sizeof(info))) {
+ return -1;
+ }
+ return 0;
+}
+
diff --git a/time.txt b/time.txt
new file mode 100644
index 0000000..7ed6ff8
--- /dev/null
+++ b/time.txt
@@ -0,0 +1 @@
+5
diff --git a/user/trace.c b/user/trace.c
index dd77760..7de5bb6 100644
--- a/user/trace.c
+++ b/user/trace.c
@@ -7,21 +7,21 @@ int
main(int argc, char *argv[])
{
int i;
- char *nargv[MAXARG];
+ char *nargv[MAXARG];

if(argc < 3 || (argv[1][0] < '0' || argv[1][0] > '9')){
fprintf(2, "Usage: %s mask command\n", argv[0]);
exit(1);
}

- if (trace(atoi(argv[1])) < 0) {
+ if (trace(atoi(argv[1])) < 0) { // 系统调用trace,设置mask值为argv[1]
fprintf(2, "%s: trace failed\n", argv[0]);
exit(1);
}

- for(i = 2; i < argc && i < MAXARG; i++){
+ for(i = 2; i < argc && i < MAXARG; i++) { // 提取command
nargv[i-2] = argv[i];
}
- exec(nargv[0], nargv);
+ exec(nargv[0], nargv); // exec command
exit(0);
}
diff --git a/user/user.h b/user/user.h
index b71ecda..6ba24e6 100644
--- a/user/user.h
+++ b/user/user.h
@@ -1,5 +1,6 @@
struct stat;
struct rtcdate;
+struct sysinfo;

// system calls
int fork(void);
@@ -23,6 +24,8 @@ int getpid(void);
char* sbrk(int);
int sleep(int);
int uptime(void);
+int trace(int);
+int sysinfo(struct sysinfo*);

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