实验的代码放在了Github上。

第二个实验是Lab: system calls
这个实验主要就是自己实现几个简单的系统调用并添加到XV6中。

XV6系统调用

添加系统调用主要有以下几步:

user/user.h中添加系统调用函数的定义。

user/usys.pl中添加入口,这个文件将会在make后生成user/usys.S文件,在该汇编文件中,每个函数就只有三行,将系统调用号通过li(load imm)存入a7寄存器,之后使用ecall进入内核态,最后返回。

fork:
 li a7, SYS_fork
 ecall
 ret

kernel/syscall.h中定义系统调用号。

kernel/syscall.csyscalls函数指针数组中添加对应的函数。在syscall函数中,先读取trapframe->a7获取系统调用号,之后根据该系统调用号查找syscalls数组中的对应的处理函数并调用。

System call tracing (moderate)

先在proc结构体中添加一个trace_mask字段,之后在fork函数中复制该字段到新进程。

在系统调用sys_trace中就只要通过argint函数读取参数,然后设置给trace_mask字段就行了。

最后修改syscall,当系统调用号和trace_mask匹配时就打印相关信息。

// proc.h
struct proc {
  ...
  // this is for sys_trace()
  uint trace_mask;
};

// proc.c
int
fork(void)	fork(void)
{
  ...
  // copy trace mask
  np->trace_mask = p->trace_mask;
  ...
}

// sysproc.c
uint64
sys_trace(void)
{
  uint mask;
  if(argint(0, (int*)&mask) < 0)
    return -1;
  struct proc *p = myproc();
  p->trace_mask |= mask;
  return 0;
}

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

  num = p->trapframe->a7;
  if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
    uint64 ret = syscalls[num]();
    p->trapframe->a0 = ret;
    if((1 << num) & p->trace_mask) {
      printf("%d: syscall %s -> %d\n", p->pid, syscall_name[num], ret);
    }
  } else {
    printf("%d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

Sysinfo (moderate)

这一个系统调用主要就是要实现freememnproc两个函数来统计内存和进程。

// sysproc.c
uint64
sys_sysinfo(void)
{
  uint64 info; // user pointer
  struct sysinfo kinfo;
  struct proc *p = myproc();
  if(argaddr(0, &info) < 0){
    return -1;
  }
  kinfo.freemem = freemem();
  kinfo.nproc = nproc();
  if(copyout(p->pagetable, info, (char*)&kinfo, sizeof(kinfo)) < 0){
    return -1;
  }
  return 0;
}

阅读kallockfree两个函数就可以知道,kmem.freelist是一个保存了当前空闲内存块的链表,因此只需要统计这个链表的长度再乘以PGSIZE就可以得到空闲内存。

// kalloc.c
uint64
freemem(void)
{
  uint64 counter = 0;
  struct run *r;
  acquire(&kmem.lock);
  r = kmem.freelist;
  while(r){
    r = r->next;
    ++counter;
  }
  release(&kmem.lock);
  return counter * PGSIZE;
}

阅读procdump和相关代码就可以知道,XV6的进程结构体保存在proc[NPROC]数组当中。而proc->state字段保存了PCB的当前状态,有UNUSED、SLEEPING、RUNNABLE、RUNNING、ZOMBIE五种状态。因此只需要遍历这个数组,然后统计state不是UNUSED状态的就行了。

// proc.c
uint64
nproc(void)
{
  uint64 counter = 0;
  struct proc *p;

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

版权声明:本文为weijunji原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/weijunji/p/14338408.html