MIT6.S081

MIT6.S081 syscall

Posted on 2021-03-16,5 min read

syscall的原理:

在shell中,当输入某个命令,比如sysinfo,shell fork出一个进程调用sysinfo.c处理sysinfo命令,此时还处于user space;在sysinfo.c的main函数中,会进行系统调用,比如系统调用sysinfo,read,kill等,这些系统调用是通过usys.pl脚本,把sysinfo通过ecall跳转到kernel的部分。

sub entry {
    my $name = shift;
    print ".global $name\n";
    print "${name}:\n";
    print " li a7, SYS_${name}\n";
    print " ecall\n";
    print " ret\n";
}
	
entry("fork");
entry("exit");
entry("wait");
entry("pipe");
entry("sysinfo");

在kernel的syscall里面查SYS_sysinfo的函数位置,如何查找位置呢,kernel/syscall.c有一个

static uint64 (*syscalls[])(void) = {
[SYS_fork]    sys_fork,
[SYS_exit]    sys_exit,
[SYS_wait]    sys_wait,
[SYS_pipe]    sys_pipe
}

可以通过SYSinfo找到对应的sys_sysinfo kernel中的函数,进行操作。

System call tracing

添加一个trace的系统调用

sysproc.c

uint64
sys_trace(void)
{
  int mask;
  if(argint(0, &mask) < 0)
    return -1;
  myproc()->trace_mask = mask; //进程信息中记录mask
  return 0;
}

proc.h


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

  // p->lock must be held when using these:
  enum procstate state;        // Process state
  struct proc *parent;         // Parent process
  void *chan;                  // If non-zero, sleeping on chan
  int killed;                  // If non-zero, have been killed
  int xstate;                  // Exit status to be returned to parent's wait
  int pid;                     // Process ID

  // these are private to the process, so p->lock need not be held.
  uint64 kstack;               // Virtual address of kernel stack
  uint64 sz;                   // Size of process memory (bytes)
  pagetable_t pagetable;       // User page table
  struct trapframe *trapframe; // data page for trampoline.S
  struct context context;      // swtch() here to run process
  struct file *ofile[NOFILE];  // Open files
  struct inode *cwd;           // Current directory
  char name[16];               // Process name (debugging)
  int trace_mask;               //记录trace mask

};

syscall.c


static uint64 (*syscalls[])(void) = {
[SYS_fork]    sys_fork,
...
[SYS_sysinfo] sys_sysinfo,
};


char* syscall_name[23] = {
        "0",
        "syscall fork",  
        "syscall exit",  
        "syscall wait",  
        "syscall pipe",  
        "syscall read",
        "syscall kill",
        "syscall exec",
        "syscall fstat",
        "syscall chdir",
        "syscall dup",
        "syscall getpid",
        "syscall sbrk",
        "syscall sleep",
        "syscall uptime",
        "syscall open",
        "syscall write",
        "syscall mknod",
        "syscall unlink",
        "syscall link",
        "syscall mkdir",
        "syscall close",
        "syscall 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]();
    int trace_mask = p->trace_mask;
	//判断是否对该syscall显示trace
    if(trace_mask >> num & 0x1){
        printf("%d: %s -> %d\n", p->pid, syscall_name[num], p->trapframe->a0);
    }
    
  } else {
    printf("%d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}



处理子进程,让mask能够继承
proc.c

int
fork(void)
{
  int i, pid;
  struct proc *np;
  struct proc *p = myproc();

  // Allocate process.
  if((np = allocproc()) == 0){
    return -1;
  }

  // Copy user memory from parent to child.
  if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){
    freeproc(np);
    release(&np->lock);
    return -1;
  }
  np->sz = p->sz;

  np->parent = p;

  // copy saved user registers.
  *(np->trapframe) = *(p->trapframe);

  // Cause fork to return 0 in the child.
  np->trapframe->a0 = 0;

  // increment reference counts on open file descriptors.
  for(i = 0; i < NOFILE; i++)
    if(p->ofile[i])
      np->ofile[i] = filedup(p->ofile[i]);
  np->cwd = idup(p->cwd);

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

  pid = np->pid;

  np->state = RUNNABLE;

  np->trace_mask = p->trace_mask; //传递trace_mask

  release(&np->lock);

  return pid;
}

Sysinfo

打印出空闲的内存和未使用的进程
· 如何获取空闲内存:遍历kmem.freelist,每个元素是一个page
· 如何获取未使用的进程:遍历proc.c中的struct proc proc[NPROC];
· 如何将上述信息从内核传递到user space:copyout

sysproc.c


uint64
sys_sysinfo(void)
{
  struct sysinfo info;
  uint64 addr;
  // 获取用户态传入的sysinfo结构体
  if (argaddr(0, &addr) < 0)
    return -1;
  struct proc* p = myproc();
  info.freemem = free_memory();
  info.nproc = proc_unused_num();
  
  //copyout 传入user pagetable,info结构体的user space地址,把kernel中的info copy过去
  if (copyout(p->pagetable, addr, (char*)&info, sizeof(info)) < 0)
    return -1;
  return 0;
}

下一篇: MIT6.S081 util→