MIT 6.1810: system calls

警告
本文最后更新于 2023-03-06,文中内容可能已过时。

MIT 6.1810 中第二个 lab 的 solution

system calls

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.

在Makefile中添加$U/_trace\,在user/usys.pl添加entry(“trace”)作为stub,在user/user.h写上系统调用的函数prototypeint trace(int);,在kernrl/syscall.h上添加#define SYS_trace 22这个系统调用的宏定义。

这里可以看出,这里的usys.pl和user.h是用来指明trace这个系统调用,以及它的函数prototype是什么,usys.pl用于生成usys.S这个文件,这个文件包含一切调用系统调用的RISC-V汇编代码。

1
2
3
4
fork:
 li a7, SYS_fork
 ecall
 ret

并不是系统调用具体的汇编代码实现,那样的文件应该在kernel文件夹内产生。这是一个一个ecall跳转。syscall.h就是一个系统调用的宏定义文件, 这里的SYS_fork就是那个文件里的一个宏,所以要想先编译通过,而不考虑trace的具体实现,这个宏得加上。

在kernel/sysproc.c中添加sys_trace()函数,这里的函数用于把要传递给系统调用的参数进行一步处理。

1
2
3
4
5
6
7
8
9
uint64
sys_trace(void)
{
  struct proc *p = myproc();
  int MASk;
  argint(0, &MASk);
  p->MASK = MASk;
  return 0;
}

本来一开始我是void,return也是直接return没有return 0。但是这样无返回值的话不能过trace all的测试。

在kernel/proc.h文件中在proc结构体中添加一个变量MASK,用来将trace得到的MASK传递给其子进程。

在kernel/proc.c中找到fork系统调用的实现部分,可以看到在前面的代码是用来将父进程的内存复制给子进程,在中间加上一句:

1
np->MASK = p->MASK;

在kernel/syscall.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
static char* syslist[] = {
  "fork",
  "exit",
  "wait",
  "pipe",
  "read",
  "kill",
  "exec",
  "fstat",
  "chdir",
  "dup",
  "getpid",
  "sbrk",
  "sleep",
  "uptime",
  "open",
  "write",
  "mknod",
  "unlink",
  "link",
  "mkdir",
  "close",
  "trace",
};

在同文件的syscall函数中系统调用真正执行的那部分后面添加:

1
2
3
if((1 << num) & p->MASK){
    printf("%d: syscall %s -> %d\n", p->pid, syslist[num-1], p->trapframe->a0);
}

lab评测:

1
2
3
4
5
6
7
$ ./grade-lab-syscall trace
make: 'kernel/kernel' is up to date.
== Test trace 32 grep == trace 32 grep: OK (0.9s)
== Test trace all grep == trace all grep: OK (1.0s)
    (Old xv6.out.trace_all_grep failure log removed)
== Test trace nothing == trace nothing: OK (1.0s)
== Test trace children == trace children: OK (9.2s)

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”.

在Makefile中添加$U/_sysinfotest,还是按照之前的步骤把syscall编译所需要的那些文件都修改好。不过user/user.h文件需要添加的是:

1
2
struct sysinfo;
int sysinfo(struct sysinfo *);

kernel/kalloc.c文件新增一个函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
uint64
memcol(void){
  struct run *r;
  uint64 col = 0;

  acquire(&kmem.lock);
  r = kmem.freelist;
  while(r) {
    r = r->next;
    col++;
  }
  release(&kmem.lock);

  return col * PGSIZE;
}

这个函数中使用的kmem和run结构体都是什么,可以在同文件的其他函数中大抵发现其含义。比如看看那个kalloc函数

kernel/proc.c文件新增一个函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
uint64
pnumcol(void){
  struct proc *p;
  uint64 col = 0;

  for(p = proc; p < &proc[NPROC]; p++) {
    acquire(&p->lock);
    if(p->state != UNUSED)  col++;
    release(&p->lock);
  }

  return col;
}

kernel/defs.h文件需要加上那两个函数的声明。

kernel/sysproc.c文件新增一个函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
uint64
sys_sysinfo(void){
  struct sysinfo si;
  uint64 addr;

  argaddr(0, &addr);
  si.nproc = pnumcol();
  si.freemem = memcol();

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

  return 0;
}

lab评测:

1
2
3
$ ./grade-lab-syscall sysinfotest
make: 'kernel/kernel' is up to date.
== Test sysinfotest == sysinfotest: OK (1.5s)