xv6-lab2-syscall

Lab: system calls(HITSZ)

前置知识

  • 阅读xv6 book 章节2、4.3、4.4
  • 熟悉系统调用用户空间代码 user/user.huser/usus.pl
  • 熟悉系统调用内核空间代码 kernel/syscall.hkernel/syscall.c
  • 熟悉进程相关代码 kernel/proc.hkernel/proc.c

System call tracing

实验目标

实现一个系统调用 trace(uint64 mask)

mask的二进制表示中第n位为1, 则表示第n号系统调用将被追踪,如trace(1<<SYS_fork) 则表示追踪 系统调用fork

追踪打印 PID: sys_$name(arg0) -return_value

完成trace的编写后,在xv6中应该看到如下的输出

[cs@localhost xv6-labs-2020] $ make qemu

……

xv6 kernel is booting

hart 2 starting

hart 1 starting

init: starting sh

$

$ trace 32 grep hello README

3: sys_read(3) -> 1023

3: sys_read(3) -> 966

3: sys_read(3) -> 70

3: sys_read(3) -> 0

实验实现

接下来根据 hints 一步一步实现

  • 在Makeifle 中的UPROGS下新增 $U/_trace

  • 在系统调用相关部分 user/user.huser/usus.plkernel/syscall.hkernel/syscall.c 新增关于trace的声明

/*	user/user.h	*/
int trace(int);

/*	user/usys.pl	*/
entry("trace");

/*	kernel/syscall.h	*/
#define SYS_trace 22

/*	kernel/syscall.c	*/
extern uint64 sys_trace(void);

static uint64 (*syscalls[])(void) = {
[SYS_fork]    sys_fork,
...
[SYS_trace]   sys_trace,
};
  • kernel/sysproc.c 中定义 系统调用 uint64 sys_trace(void) ,并在结构体proc中增加变量来记录 mask
/*	kernel/sysproc.h	*/
uint64
sys_trace(void)
{
  int n;
  //获取追踪的mask
  if(argint(0, &n) < 0)
    return -1;
  //将mask保存在本进程的proc中
  myproc()->trace_mask = n;
  return 0;
}

/*	kernel/proc.h	*/

struct proc {
  struct spinlock lock;
...
  //因为系统调用只有二十几个所以用4字节的int即可全部覆盖
  int trace_mask;                 // Mask for trace
};
  • 修改 fork() 以保证子进程继承了父进程的 mask
/*	kernel/proc.c	*/
int
fork(void)
{
  int i, pid;
  struct proc *np;
  struct proc *p = myproc();

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

  //copy trace mask
  np->trace_mask = p->trace_mask;
  ...
}
  • 修改 kernel/syscall.c 中的 syscall() 来打印所需信息
/*	kernel/syscall.c	*/

//创建一个字符串数组来储存系统调用名
static char syscall_name[23][16] = {"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();

  //系统调用号储存在a7
  num = p->trapframe->a7;
  //第一个参数储存在a0
  int first_arg = p->trapframe->a0;
  if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
    //返回值储存在a0
    p->trapframe->a0 = syscalls[num]();
    //位操作判断mask是否覆盖了当前调用号
    if(p->trace_mask > 0 && (p->trace_mask&(1<<num)))
    {
      printf("%d: sys_%s(%d) -> %d\n", p->pid, syscall_name[num-1], first_arg, p->trapframe->a0);
    }
  } else {
    printf("%d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

Sysinfo

实验目标

实现一个系统调用 sysinfo() 返回系统当前 可用内存state为UNUSED的进程数当前进程空闲的文件描述符数量

实验实现

接下来根据hints一步一步实现

  • 同上一个实验一样增加相应的函数声明

  • kernel/user.h 中提前声明 struct sysinfo

  • 参考 kernel/sysfile.c/sys_fstatkernel/file.c/filestat 使用 copyput() 将内核数据传输到用户态

/*	kernel/sysproc.c	*/
uint64
sys_sysinfo(void)
{

  uint64 addr;
  if(argaddr(0, &addr) < 0)
    return -1;
  struct sysinfo info;
  info.freemem = get_free_mem();
  info.nproc = get_proc_num();
  info.freefd = get_free_fd();
    
  //copyout 参数:进程页表,用户态目标地址,数据源地址,数据大小
  //返回值:数据大小
  if(copyout(myproc()->pagetable, addr, (char *)&info, sizeof(info)) < 0)
    return -1;
  
  return 0;
}
  • kernel/kalloc.c、proc.c 中实现三个收集数据的函数
/*	kernel/kalloc.c	*/
/*
	观察kalloc.c可以得知空闲内存由链表 kmem.list 给出,每一个节点代表一页内存
	每页内存大小由 宏定义PGSIZE 给出
	注意锁的获取和释放
*/
uint64
get_free_mem(void)
{
  struct run *r;
  acquire(&kmem.lock);
  r = kmem.freelist;
  int num = 0;
  while(r)
  {
    ++num;
    r = r->next;
  }
  release(&kmem.lock);
  return num * PGSIZE;

}

/*	kernel/proc.c	*/

//sysinfo
int
get_proc_num(void){
  
  struct proc *p;
  int num = 0;
  for(p = proc; p < &proc[NPROC]; p++) {
    acquire(&p->lock);
    if(p->state == UNUSED) {
      ++num;
    }
    release(&p->lock);
  }
  return num;
}

uint64
get_free_fd(void)
{
  uint64 num = 0;
  int fd;
  struct proc *p = myproc();

  for(fd = 0; fd < NOFILE; fd++){
    if(p->ofile[fd] == 0){
      ++num;
    }
  }
  return num;
}

/*	kernel/defs.h	*/
/*
	在defs.h中增加三个函数的声明以便sysinfo调用
*/

uint64          get_free_fd(void);
uint64          get_free_mem(void);
int             get_proc_num(void);

实验原理

usys.pl

注意到其中的脚本代码

sub entry {
    my $name = shift;
    print ".global $name\n";
    print "${name}:\n";
    print " li a7, SYS_${name}\n";	//将系统调用号放入a7寄存器
    print " ecall\n";				//调用ecall进入内核
    print " ret\n";
}

它说明了我们实现的系统调用如何在汇编中调用

参数传递

系统调用通过寄存器传递参数,储存在用户态的上下文 trapframe

实验结果

请添加图片描述

  • 10
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值