Lab: System Calls

System call tracing

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.

需要自己实现一个叫trace的syscall,在user/trace.c中有调用trace的源码,而我们的目的就是让这一段代码能够跑通,并且输出我们希望打印的系统调用函数的结果。

trace接受一个整数参数trace_mask,通过二进制位的标记来输出系统调用的结果。例如trace 32,表示输出read函数的调用结果,因为(1 << SYS_read)是32,输出形式如下:pid: syscall read → ret

注册用户程序trace

首先想要调用user/trace.c,需要在Makefile的UPROGS下添加$U/_trace。

添加系统调用trace函数原型

在user/user.h中添加函数原型。

//sys_call
int fork(void);
...
int update(int);
int trace(int);

添加ecall入口

在user/usys.pl中添加ecall调用trace的入口

...
entry("update");
entry("trace");

设置系统调用的序号

在kernel/syscall.h中定义trace函数的序号

...
#define SYS_close 21
#define SYS_trace 22

为进程添加trace_mask参数

需要记录下传入的trace_mask参数我们才知道当前进程执行的系统调用是否需要输出信息。所以需要在kernel/proc.h中修改proc结构体,增加trace_mask

struct proc {
	...
	int trace_mask;  // for trace
};

实现sys_trace()

定义完了就需要实现函数,在kernel/sysproc.c中实现我们的系统调用,需要从当前进程中取得传入的trace_mask标记。

uint64
sys_trace(void)
{
    int trace_mask;
    argint(0, &trace_mask);
    if (trace_mask < 0)
        return -1;

    struct proc *p = myproc();
    p->trace_mask = trace_mask;

    return 0;
}

修改fork()

调用kernel/proc.c的fork()函数时,我们需要将父进程的track_mask传给子进程。

...
np->trace_mask = p->trace_mask;

release(&np->lock);

return pid;

修改syscall()

只有在调用syscall()时才知道到底要不要输出调用信息,已经输出调用结果。

// kernel/syscall.c

...
extern uint64 sys_trace(void);

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

static char *syscall_name[] = {
    "fork",
    "exit",
    "wait",
    "pipe",
    "read",
    "kill",
    "exec",
    "fstat",
    "chdir",
    "dup",
    "getpid",
    "sbrk",
    "sleep",
    "uptime",
    "open",
    "write",
    "open",
    "mknod",
    "unlink",
    "link",
    "mkdir",
    "close",
    "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]();
    // trace output
    if ((1 << num) & p->trace_mask)
        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;
  }
}

Sysinfo

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

做一个简单的sysinfo系统调用,输出简单的信息。

整体步骤和上面的差不多,只是需要自己实现两个统计的函数即可。

nfreemem()

在kernel/kalloc.c中添加统计可用的内存字节数的函数nfreemem()。

uint64
nfreemem()
{
    uint64 cnt = 0;
    struct run *r;
    acquire(&kmem.lock);
    r = kmem.freelist;
    while (r)
    {
        cnt++;
        r = r->next;
    }
    release(&kmem.lock);
    return cnt * PGSIZE;
}

nproc()

在kernel/proc.c中添加统计进程状态不为UNUSED的进程数函数nproc()。

uint64
nproc(void)
{
    uint64 cnt = 0;
    struct proc *p;

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

sys_sysinfo()

在kernel/sysproc.c中添加系统调用入口。

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

    argaddr(0, &addr);

    info.freemem = nfreemem();
    info.nproc = nproc();

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

    return 0;
}