6.S081/6.828: 2 Lab system calls

2022-11-26 05:02:54 浏览数 (1)

上一个Lab实现了一些shell工具,这个Lab实现一些系统调用,来帮助理解系统调用如何工作、如何暴露。

一、trace

1 问题分析

实现一个trace,能够追踪某个进程(包括子进程)指定系统调用执行情况,打印它的状态,如下图所示。

image.pngimage.png
  1. 传递mask来表示追踪的是哪个系统调用,例如:mask=1<<SYS_fork。
  2. 打印进程及其子进程运行某个系统调用的日志,所以mask应该添加在进程结构体中,并且fork内核函数也需要copy mask字段。

主要步骤如下:

  1. kernel/proc.h添加tracemask字段。
  2. kernel/sysproc.c添加sys_trace函数。
  3. kernel/syscall.h添加系统调用号。
  4. kernel/syscall.c系统调用表中添加一项,并增加系统调用名称表。
  5. kernel/syscall.c中修改syscall函数。
  6. 在user/user.h中暴露该系统调用,在user/usys.pl中增加一项,用于生成汇编代码触发中断。

2 代码实现

代码语言:c复制
//代码比较分散,只介绍主要代码
uint64
sys_trace(void)
{
  int mask;

  if(argint(0, &mask) < 0)
    return -1;
  myproc()->tracemask=mask;
  return 0;
}


static char* syscallnames[] = {
[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",
};
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]();
    if (p->tracemask&(1<<num)){
      printf("%d: %s %s %s %dn",p->pid,"syscall",syscallnames[num],"->",p->trapframe->a0);
    }
    
  } else {
    printf("%d %s: unknown sys call %dn",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

二、sysinfo

1 问题分析

sysinfo系统调用用来统计当前空闲物理内存、存在的进程数量,我们需要对内存分配器、进程管理有一定的了解。

  1. xv6的物理内存是用空闲链表管理的,统计这条链表上的页数就行。
  2. xv6的进程分配是采用一个进程数组来维护,直接遍历这个数组上非UNUSED状态的进程即可。
  3. 用户态向内核传递数据,会存放在寄存器中,而内核trap代码会将寄存器保存在进程trap frame中。内核中有argint、argaddr、argfd,支持读取整数、指针、文件描述符,都是argraw的封装。
  4. 内核态和用户态不会直接读写同一份数据,而是会拷贝,copyout是将数据拷贝到用户地址中。

2 内存分配和进程管理

代码语言:c复制
struct run {
  struct run *next;
};

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


struct cpu cpus[NCPU];
//进程管理数组
struct proc proc[NPROC];

enum procstate { UNUSED, USED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };

每个run表示一个物理页,它的指针就是物理页起始地址,每一物理页都会存放下一物理页的地址,来减少辅助空间。进程是采用一个数组来管理。

3 代码实现

代码语言:c复制
uint64
sys_sysinfo(void)
{
  struct sysinfo info;
  info.freemem=count_free_memory();
  info.nproc=count_proc();

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


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

//计算进程数量
uint64 count_proc(void){
  int count=0;
  for(int i=0;i< NPROC ;i  ){
    if(proc[i].state!=UNUSED){
      count  ;
    }
  } 
  return count;
}

0 人点赞