6.S081 lab2

6.S081 lab2

Preparation

xv6 book 部分

这一部分最核心的我觉得是x6机器从通电到创建第一个进程的过程:
1.通电,初始化然后加载bootloader,boot loader 把 kernel的代码加载到内存。
2.kernel代码从一个叫entry.S的汇编代码开始执行,它初始化了一个栈,可以让xv6运行c语言代码。然后他就call一个叫stat的c函数处。
3. start函数首先执行一些只有在机器模式才能做的配置,然后切换到监督模式(supervisor mode),并跳转到了main函数。
4. main函数初始几个设备和子系统,然后调用 userinit创建第一个进程。userinit通过调用 initcode.S,然后initcode.S又通过exec这个系统调用进入内核。exec执行完成后,返回到init进程,他会创建shell。

源码阅读

在读这个lab要求的源码的时候,在kernel/syscall.c中发现个奇怪的语法

static uint64 (*syscalls[])(void) = {
[SYS_fork]    sys_fork,
[SYS_exit]    sys_exit,
[SYS_wait]    sys_wait,
[SYS_pipe]    sys_pipe,
[SYS_read]    sys_read,
[SYS_kill]    sys_kill,
[SYS_exec]    sys_exec,
[SYS_fstat]   sys_fstat,
[SYS_chdir]   sys_chdir,
[SYS_dup]     sys_dup,
[SYS_getpid]  sys_getpid,
[SYS_sbrk]    sys_sbrk,
[SYS_sleep]   sys_sleep,
[SYS_uptime]  sys_uptime,
[SYS_open]    sys_open,
[SYS_write]   sys_write,
[SYS_mknod]   sys_mknod,
[SYS_unlink]  sys_unlink,
[SYS_link]    sys_link,
[SYS_mkdir]   sys_mkdir,
[SYS_close]   sys_close,
[SYS_trace]   sys_trace,
[SYS_sysinfo]   sys_sysinfo,
};

这是定义了一个函数指针数组变量,然后初始化,[ ]是在指定下标赋值。

System call tracing

目标是要我们写一个系统调用,来参数mask指定的系统调用,并输出 进程号,系统调用名称以及该调用的返回值。
首先要知道内核是通过查看 a7中的数据来确定是调用哪个系统调用的,然后执行该系统调用并把值存到a0中。

我们首先要修改 kernel/proc.c 中的proc结构,增加一个参数来保存mask,这个参数可以是数组也可以是其他东西,我这里用的是int

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 mask;			//trace的参数
};

然后在kernel/sysproc.c中增加一个系统调用函数,到时候syscall.c会调用这个函数。

uint64
sys_trace(void)
{
  struct proc *p = myproc();
  int n;
  if(argint(0, &n) < 0)
    return -1;
  p->mask=n;
  return 0; 
}

这个函数很简单,就是把读取mask参数并保存到proc里。
然后要修改fork()和syscall().
fork()是要让他在fork的时候也要复制上一个进程的mask信息

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;
  np->mask=p->mask;
  // 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;

  release(&np->lock);

  return pid;
}

syscall( )是要在系统调用的时候根据mask判断要不要printf。
当然为了方便输出,用一个数组保存了那些系统调用名

static char* syscall_names[24]={"","fork","exit","wait","pipe","read","kill",
"exec","fstat","chdir","dup","getpid","sbrk","sleep","uptime","open","write",
"mknod","unlink","link","mkdir","close","trace","sysinfo"
};
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->mask)&(1<<(p->trapframe->a7)))!=0){
    printf("%d: syscall %s -> %d\n",p->pid,syscall_names[p->trapframe->a7],p->trapframe->a0);
    }
    
  } else {
    printf("%d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

Sysinfo

在 kernel/kalloc.c中增加一个函数,用来计算剩余的内存大小。这个直接遍历页表freelist的数量,然后乘上页大小4096就好了。

int 
freemem(void){
  int cnt=0;
  struct run *r;
  r = kmem.freelist;
  while(r){
  cnt+=4096;
  r=r->next;
  }
  //printf("freemem: %d\n",cnt);
  return cnt;
}

在kernel/proc.c加一个函数,计算题意要求的进程数目。

int
nproc(void){
  int cnt=0;
  struct proc *p;
  for(p = proc; p < &proc[NPROC]; p++){
    if(p->state!=UNUSED){
      cnt+=1;
    }
  }
  //printf("nproc: %d\n",cnt);
  return cnt;

}

最后在kernel/sysproc.c中增加sysinfo( )函数。

uint64
sys_sysinfo(void){
  //printf("sys_sysinfo\n");
  struct proc *p = myproc();
  uint64 addr;
  if(argaddr(0, &addr) < 0)
    return -1;
  /*
  struct sysinfo info;
  info.freemem=freemem();
  info.nproc=nproc();
  printf("free:%d    nproc:%d\n",info.freemem,info.nproc);
  if((copyout(p->pagetable, addr, (char *)&info, sizeof(info))<0))
    return -1;
  */
  uint64 info[2]={freemem(),nproc()};
  printf("free:%d    nproc:%d\n",info[0],info[1]);
  if((copyout(p->pagetable, addr, (char *)info, sizeof(info))<0))
    return -1;
  
  return 0;
}

注意 struct sysinfo结构体是两个unit64的数,所以用数组存的话注意数组也要用unit64。

总结

这个lab一开始写的时候比较艰难,主要是自己没弄清系统调用的流程,弄清之后就比较容易了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值