Lab2系统调用

0. 实验前:切换至Lab2分支

在xv6-labs-2021文件夹下运行终端,并且运行以下命令:

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

948c6bfee5f228d8f6f19079569afda

切换成功。

1. 新的系统调用

1.1 System call tracing

1
In this assignment you will add a system call tracing feature that may help you when debugging later labs. You'll create a new trace system call that will control tracing. It should take one argument, an integer "mask", whose bits specify which system calls to trace. For example, to trace the fork system call, a program calls trace(1 << SYS_fork), where SYS_fork is a syscall number from kernel/syscall.h. You have to modify the xv6 kernel to print out a line when each system call is about to return, if the system call's number is set in the mask. The line should contain the process id, the name of the system call and the return value; you don't need to print the system call arguments. The trace system call should enable tracing for the process that calls it and any children that it subsequently forks, but should not affect other processes.
1.1.1 解题过程
  • 在Makefile UPROGS下新增$U/_trace\

5ccb71e4769793835d75c6131a805b1

  • 执行make qemu,结果如下:

image-20221008153655758

  • 为user/user.h ; user/usys.pl ; kernel/syscall.h 添加trace的原型或声明

b2bf222b8c752a4d16957cd7ae69709

774834c92a766c5e10534e243515424

98e98fb88ef1977f72bd13229f19fa3

make qemu 成功,但是执行trace指令失败:

ee38ff31b574fdf8b9fc128aa80c29a

  • kernel/sysproc.c 、 kernel/proc.h 、 kernel/syscall.c 加入对应的实现代码

1f7dd88e5242673d30877a11c082393

2e7591aa3468e86ba9a1a8b4a3efffe

image-20221008154502843

  • 修改kernel/proc.c fork(),以便子孙进程也能够被追踪

image-20221008155623707

  • 修改 kernel/syscall.c 的 syscall(),以输出相应的跟踪信息

image-20221008155802702

此时结果:

76f0780d4435f2868753c3f70666fc6

发现进程名有误,再次修改syscall(),添加一个字符数组,用以对相关系统调用名的打印输出:

image-20221008155903900

1.1.2 代码
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
// Makefile

UPROGS=\
...
$U/_trace

// user/user.h

int trace(int);

// kernel/syscall.h

#define SYS_trace 22

// user/usys.pl

entry("trace");

// kernel/syscall.c

extern uint64 sys_trace(void);

static uint64 (*syscalls[])(void) = {
[SYS_fork] sys_fork,
...
[SYS_trace] sys_trace,
};

// kernel/sysproc.c

uint64
sys_trace(void)
{
int pid;
if(argint(0, &pid) < 0)
return -1;
myproc()->tracemask = pid;
return 0;
}

// kernel/proc.h

struct proc {
struct spinlock lock;
...
int tracemask;
};

// kernel/proc.c

int
fork(void)
{
int i, pid;
struct proc *np;
struct proc *p = myproc();
if((np = allocproc()) == 0){
return -1;
}
np->tracemask = p->tracemask;
...
}

// kernel/syscall.c

...
static char syscall_name[23][16] = {"fork", "exit", "wait", "pipe", "read", "kill", "exec", "fstat", "chdir", "dup", "getpid",
"sbrk", "sleep", "uptime", "open", "write", "mknod", "unlink", "link", "mkdir", "close", "trace",
"sysinfo"};

void
syscall(void)
{
int num;
struct proc *p = myproc();

num = p->trapframe->a7;
if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
p->trapframe->a0 = syscalls[num]();
if(p->tracemask > 0 && (p->tracemask&(1<<num))) {
printf("%d: syscall %s -> %d\n", p->pid, syscall_name[num-1], p->trapframe->a0);
}
} else {
printf("%d %s: unknown sys call %d\n",
p->pid, p->name, num);
p->trapframe->a0 = -1;
}
}
1.1.3 运行结果测试

fb959b82718d00e39e4630093ac2358

1.2 Sysinfo

1
In this assignment you will add a system call, sysinfo, that collects information about the running system. The system call takes one argument: a pointer to a struct sysinfo (see kernel/sysinfo.h). The kernel should fill out the fields of this struct: the freemem field should be set to the number of bytes of free memory, and the nproc field should be set to the number of processes whose state is not UNUSED. We provide a test program sysinfotest; you pass this assignment if it prints "sysinfotest: OK".
1.2.1 解题过程
  • 在Makefile UPROGS下新增$U/_sysinfotest\

    866974a3cf0bcb58fc73123d125ab1a

  • 为user/user.h ; user/usys.pl ; kernel/syscall.h 添加trace的原型或声明

69a98164633fe236b86151fbeca7ce6

image-20221008180246990

image-20221008180310813

  • 声明并定义state为UNUSED的进程数、当前进程空闲的文件描述符数量的函数

83958fb589a4fc74925010c4c019b02

c223e2657cce6e93c132492067a296f

e6129d50ea917d6dada7b3a8b724b6b

  • 定义sysinfo

image-20221008181651174

1.2.2 代码
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
// Makefile

UPROGS=\
...
$U/_sysinfotest

// user/user.h

struct sysinfo;
...
int sysinfo(struct sysinfo *);

// kernel/syscall.h

#define SYS_sysinfo 23

// user/usys.pl

entry("sysinfo");


// kernel/syscall.c

extern uint64 sys_sysinfo(void);

static uint64 (*syscalls[])(void) = {
[SYS_fork] sys_fork,
...
[SYS_sysinfo] sys_sysinfo,
};

// kernel/sysproc.c
#include "sysinfo.h"

uint64
sys_sysinfo(void)
{
uint64 addr;
if (argaddr(0, &addr) < 0)
return -1;

struct proc *p = myproc();
struct sysinfo si;

si.freemem = getfreemem();
si.nproc = getnproc();

if (copyout(p->pagetable, addr, (char *)&si, sizeof(si)) < 0)
return -1;

return 0;
}

// kernel/proc.c

uint64
getnproc(void)
{
uint64 n = 0;
struct proc *p;
for (p = proc; p < &proc[NPROC]; ++p) {
if (p->state != UNUSED) ++n;
}
return n;
}
1.2.3 运行结果测试

image-20221008181742684

2.实验心得

​ 首先,需要了解清楚xv6的源码的某个函数的具体功能,以及源码与源码之间的关系,才能够好好地新增新的系统调用;并且,当完成一个实验的输出后,需要及时与官方文档中的结果进行比对,查看输出结果的区别在于哪个变量的值,如本次实验1中我第一次的结果缺少了系统调用名字的数组以及引用,导致所有系统调用名都是父程序的调用名,而缺少了子孙程序的调用名;最后,一定要记得引入需要用到的头文件,否则将需要很多的时间对函数进行检查,却找不到报错的原因。