Lab 2 syscall

System call tracing

该题要实现一个系统调用跟踪功能,创建一个新的系统调用trace来控制跟踪。

在用户层面的系统调用trace已经写好,在user/trace.c中。

首先按照提示增加相应的系统调用原型、存根、系统调用编号等内容。通过在kernel/sysproc.c中添加一个sys_trace()函数来实现系统调用。

  1. uint64
  2. sys_trace(void)
  3. {
  4. int mask;
  5. if(argint(0, &mask) < 0)
  6. return -1;
  7. myproc()->mask = mask;
  8. return 0;
  9. }

对于mask,根据提示中可以知道,将参数保存到proc结构体中来实现新的系统调用,所以,在proc结构体中将其补上。接着需要修改fork()将跟踪mask从父进程复制到子进程,这里很简单,只要直接赋值就好。

对于系统调用的实现,需要改写syscall()函数,打印跟踪的输出,添加一个系统调用名称数组以建立索引,实现打印系统调用名称的功能。

static char *syscallname[] = {
[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]  "sgetpid",
[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",
};

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->mask) & (1 << num))
    {
      printf("%d: syscall %s -> %d\n", p->pid, syscallname[num], p->trapframe->a0);
    }
  } else {
    printf("%d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

Sysinfo

首先,按照“System call tracing”中的步骤添加相应的内容,以及hints中的对struct sysinfo的声明,使得编译成功。

根据hints,我们知道sysinfo需要将一个struct sysinfo复制回用户空间,且提示告诉我们要参考sys_fstat()以及在filestat()中如何使用copyout()。于是我们可以照葫芦画瓢,使用argadd()检索地址确保程序正常运行。同理,观察copyout(),创建一个进程以便于传入参数,按照解释我们可以知道,我们需要复制sysinfo,所以可以确定对应的参数,如果复制失败,则返回-1。

接下来是获取空闲的内存量,观察kernel/kalloc.c中的内容,可以知道这里建立了结构体以存储内存,类似于一个页表,且对于每一页,有4096个字节(PGSIZE)。所以可以采用确定页数,然后求出內存的方法。

uint64
free_mem(void)
{
  struct run *p = kmem.freelist;
  int n = 0;
  while(p)
  {
    n++;
    p = p->next;
  }
  return n * PGSIZE;
}

然后是获取进程数,对于每一个进程,只要状态state不是UNUSED的,就应该被计算入其中,于是可以很容易得到代码。

uint64
proc_size(void)
{
  int n = 0;
  for(int i = 0; i < NPROC; i++)
  {
    if(proc[i].state != UNUSED) n++;
  }
  return n;
}

最后是sys_info的代码,将上述内容结合起来。

uint64
sys_info(void)
{
  struct sysinfo info;
  uint64 addr;
  struct proc *p = myproc();

  info.freemem = free_mem();
  info.nproc = proc_size();

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