一、sys_trace系统调用实现

我们首先再proc结构体中添加一个数据字段,用于保存trace的参数。并在sys_trace()的实现中实现参数的保存

// kernel/proc.h
struct proc {
  // ...
  int trace_mask;    // trace系统调用参数
};

// kernel/sysproc.c
uint64
sys_trace(void)
{
  // 获取系统调用的参数
  argint(0, &(myproc()->trace_mask));
  return 0;
}

由于struct proc中增加了一个新的变量,当fork的时候我们也需要将这个变量传递到子进程中

//kernel/proc.c
int
fork(void)
{
  // ...

  safestrcpy(np->name, p->name, sizeof(p->name));

  //将trace_mask拷贝到子进程
  np->trace_mask = p->trace_mask;

  pid = np->pid;
  // ...

  return pid;
}

接下来应当考虑如何进行系统调用追踪了,根据提示,这将在syscall()函数中实现。

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

  num = p->trapframe->a7;  // 系统调用编号,参见书中4.3节
  if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
    p->trapframe->a0 = syscalls[num]();  // 执行系统调用,然后将返回值存入a0

    // 系统调用是否匹配
    if ((1 << num) & p->trace_mask)
      printf("%d: syscall %s -> %d\\n", p->pid, syscalls_name[num], p->trapframe->a0);
  } else {
    printf("%d %s: unknown sys call %d\\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

还需要在syscall.c中定义一些变量

// ...
extern uint64 sys_trace(void);

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

static char *syscalls_name[] = {
[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系统调用实现

我们在 kernel/proc.c 中新增函数 nproc 如下,通过该函数以获取可用进程数目:

uint64
nproc(void)
{
  struct proc *p;
  uint64 num = 0;
  for (p = proc; p < &proc[NPROC]; p++)
  {
    acquire(&p->lock);
    if (p->state != UNUSED)
    {
      num++;
    }
    release(&p->lock);
  }
  return num;
}

接下来我们来实现获取空闲内存数量的函数。可用空间的判断在kernel/kalloc.c文件中。这里定义了一个链表,每个链表都指向上一个可用空间,这里的 kmem 就是一个保存最后链表的变量。

struct run {
  struct run *next;
};

struct {
  struct spinlock lock;
  struct run *freelist;
} kmem;

在 kernel/kalloc.c 中新增函数 free_mem ,以获取空闲内存数量:

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

我们在 kernel/sysproc.c 文件中添加 sys_sysinfo 函数的具体实现如下:

uint64
sys_sysinfo(void)
{
  
  uint64 addr;
  struct sysinfo info;
  struct proc *p = myproc();
  
  // 使用argint获取用户空间地址
  argint(0, (int*)&addr); 

  // 检查地址是否有效
  if (addr == 0)
    return -1;
  
  info.freemem = free_mem();
  
  info.nproc = nproc();

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

最后在 user 目录下添加一个 sysinfo.c 用户程序:

#include "kernel/param.h"
#include "kernel/types.h"
#include "kernel/sysinfo.h"
#include "user/user.h"

int
main(int argc, char *argv[])
{
    // param error
    if (argc != 1)
    {
        fprintf(2, "Usage: %s need not param\\n", argv[0]);
        exit(1);
    }

    struct sysinfo info;
    sysinfo(&info);
    // print the sysinfo
    printf("free space: %d\\nused process: %d\\n", info.freemem, info.nproc);
    exit(0);
}