Skip to content

Lab: system calls

System call tracing

assignment要求添加一个系统的trace功能

trace 32 grep hello README

接受一个mask参数,根据mask的对应bit指定要跟踪的syscall

添加声明

# user/usys.pl
# add
entry("trace");
// user/user.h
int trace(int);
// kernel/syscall.h
#define SYS_link   19
#define SYS_mkdir  20
#define SYS_close  21

#define SYS_trace  22 // +
// kernel/syscall.c
extern uint64 sys_wait(void);
extern uint64 sys_write(void);
extern uint64 sys_uptime(void);
extern uint64 sys_trace(void); // +

static uint64 (*syscalls[])(void) = {
[SYS_fork]    sys_fork,
@@ -127,8 +128,36 @@ static uint64 (*syscalls[])(void) = {
[SYS_link]    sys_link,
[SYS_mkdir]   sys_mkdir,
[SYS_close]   sys_close,
[SYS_trace]   sys_trace, // +
};

syscall函数修改、实现sysproc.c中的函数

//kernel/syscall.c

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](); // 进行系统调用
|   uint64 ret_val = p->trapframe->a0;  // 获取系统调用返回值
|   if((p->trace_mask>>num)&1) {
|     printf("%d: syscall %s -> %d\n",p->pid, syscall_names[num], ret_val);
|   }
  } else {
    printf("%d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

对系统调用信息进行处理。

//kernel/syscall.c

// 添加系统调用名称数组
const char *syscall_names[] = {
[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",
};
// kernel/sysproc.c

uint64
sys_trace(void) 
{
  int mask;

  if(argint(0, &mask) < 0) //从用户空间获取系统调用参数(实现位于kernel/syscall.c) 
    return -1;
  myproc()->trace_mask = mask;
  return 0;
}

获取系统调用参数,并保存到proc结构体中

// kernel/proc.h

// Per-process state
struct proc {
  struct spinlock lock;

@---------------------------------------------@
  struct file *ofile[NOFILE];  // Open files
  struct inode *cwd;           // Current directory
  char name[16];               // Process name (debugging)

  int trace_mask; // + 在进程的struct中添加一个存储mask的变量 
};

多进程trace

// kernel/proc.c

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

  np->trace_mask = p->trace_mask; // + 使 子进程的mask 等于 父进程的mask

  pid = np->pid;

  release(&np->lock);

Sysinfo

添加一个系统调用:sysinfo用于收集系统的信息

// kernel/sysinfo.h

struct sysinfo {
  uint64 freemem;   // amount of free memory (bytes)
  uint64 nproc;     // number of process
};
使用该结构体存储sysinfo

可以知道需要收集以下信息: 1. 进程数量 2. 空的memory

相关声明步骤与trace一致

获取进程数量

// kernel/proc.c

// Print a process listing to console.  For debugging.
// Runs when user types ^P on console.
// No lock to avoid wedging a stuck machine further.
void
procdump(void)
{
  static char *states[] = {
  [UNUSED]    "unused",
  [SLEEPING]  "sleep ",
  [RUNNABLE]  "runble",
  [RUNNING]   "run   ",
  [ZOMBIE]    "zombie"
  };
  struct proc *p;
  char *state;

  printf("\n");
  for(p = proc; p < &proc[NPROC]; p++){
    if(p->state == UNUSED)
      continue;
    if(p->state >= 0 && p->state < NELEM(states) && states[p->state])
      state = states[p->state];
    else
      state = "???";
    printf("%d %s %s", p->pid, state, p->name);
    printf("\n");
  }
}

// +++++
uint64 num_proc() {
  uint64 cnt = 0;
  struct proc *p;
  for(p = proc; p <&proc[NPROC];p++){
    if(p->state == UNUSED) // 处于UNUSED状态就跳过
      continue;
    cnt++;
  }
  return cnt;
}
// +++++

观察可以发现,前一个函数就是一个输出所有运行进程的函数。 因而可以以相同的方式统计进程的数量。

获取空的memory大小

// Allocate one 4096-byte page of physical memory.
// Returns a pointer that the kernel can use.
// Returns 0 if the memory cannot be allocated.
void *
kalloc(void)
{
  struct run *r;

  acquire(&kmem.lock);
  r = kmem.freelist;
  if(r)
    kmem.freelist = r->next;
  release(&kmem.lock);

  if(r)
    memset((char*)r, 5, PGSIZE); // fill with junk
  return (void*)r;
}

// +++++
uint64 num_freemem() {
  struct run *r;
  uint64 mem_bytes = 0;
  r = kmem.freelist;

  while (r) {
    mem_bytes += PGSIZE;
    r = r->next;
  }

  return mem_bytes;
}
// +++++

前一个函数是用于分配memory的,可以作为参照

将sysinfo的结构体复制到user space

需要参照sys_fstat() (kernel/sysfile.c) and filestat() (kernel/file.c)来使用copyout

// kernel/sysfile.c

uint64
sys_fstat(void)
{
  struct file *f;
  uint64 st; // user pointer to struct stat

  if(argfd(0, 0, &f) < 0 || argaddr(1, &st) < 0)
    return -1;
  return filestat(f, st);
}
// kernel/file.c

// Get metadata about file f.
// addr is a user virtual address, pointing to a struct stat.
int
filestat(struct file *f, uint64 addr)
{
  struct proc *p = myproc();
  struct stat st;

  if(f->type == FD_INODE || f->type == FD_DEVICE){
    ilock(f->ip);
    stati(f->ip, &st);
    iunlock(f->ip);
    if(copyout(p->pagetable, addr, (char *)&st, sizeof(st)) < 0)
      return -1;
    return 0;
  }
  return -1;
}
// kernel/sysproc.c

uint64
sys_sysinfo(void)
{
  uint64 addr;

  if(argaddr(0, &addr) < 0) // 获得addr
    return -1;

  struct sysinfo info;          // 开头要加上#include "sysinfo.h"
  info.freemem = num_freemem(); // memory空间
  info.nproc = num_proc();      // 进程数量
  if(copyout(myproc()->pagetable, addr, (char *)&info, sizeof(info)) <0) {
    return -1;
  }
  return 0;
}