【xv6-labs】02 Lab: system calls

GDB

如何启动gdb

  • 启动 make CPUs=1 qemu-gdb
  • 在另一个终端启动 gdb-multiarch kernel/kernel
  • 进入gdb后输入 target remote localhost:26000

GDB 的一些命令

  • shell clear # 清屏
  • layout src # 显示源码
  • layout regs # 显示寄存器
  • layout split # 同时显示源码和寄存器
  • backtrace # 显示函数调用栈

在XV6 如何添加系统调用

  1. user/user.h 中添加系统调用函数的定义. 比如:
struct stat;

// system calls
int fork(void);
int exit(int) __attribute__((noreturn));
int wait(int*);
int pipe(int*);
int write(int, const void*, int);
int read(int, void*, int);
int close(int);
  1. user/usys.pl 中添加入口, 这个文件会在 make 后生成 user/usys.S 文件, 在该汇编文件中,每个函数就只有三行,将系统调用号通过 li(load imm) 存入 a7 寄存器,之后使用 ecall 进入内核态,最后返回。
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");
  1. kernel/syscall.h中定义系统调用号
#define SYS_mkdir  20
#define SYS_close  21
#define SYS_trace  22
  1. kernel/syscall.csyscalls 函数指针数值中添加对应的函数。
// An array mapping syscall numbers from syscall.h
// to the function that handles the system call.
// 这里的static uint64 (*syscalls[])(void)
// 表示定义一个 静态函数指针的数组
// 函数指针参数是void, 返回值是 uint64
// [SYS_fork]    sys_fork, 表示将第 SYS_fork 设置为 sys_fork
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,
};
  1. syscall 函数中,先读取 trapframe->a7 获取系统调用号之后,根据系统调用号查找syscalls数组中的对应的处理函数并调用。
  2. 获取传入的参数。通过往 a0寄存器写入,通过一组专门得API读取出
  3. 返回参数
p->trapframe->a0 = syscalls[num]();

System call tracing (moderate)

需要添加一个系统调用 trace 来控制追踪系统调用。
输入参数: mask 使用位来指定需要跟踪的系统调用。比如,跟踪fork, 系统调用可以是 trace(1<SYS_fork)
在这里,使用每一个位来标识是否启用跟踪,比如 SYS_fork 是 4 那么 1 << SYS_fork 的二进制就是: 10000
需要修改 xv6的内核,当相关的系统调用返回时,需要打印一行信息。这行信息包括线程ID,系统调用名字以及返回的值。不需要打印系统调用的参数。trace系统调用应该为其子线程开启跟踪,但是不能影响到无关的线程。
输出示例:

trace 32 grep hello README
threadID:syscall syscall Name -> returen value
  1. 添加一个系统调用的步骤,添加好接口trace 和系统调用sys_trace

uint64
sys_trace(void)
{
  int mask;
  argint(0, &mask);
  // 这一行在第二点里面解释
  myproc()->trace_mask = mask;
  return 0;
}

这里测试一下拿到的mask是否是对的。
2. 保存mask

在PCB结构里面增加 trace_mask字段

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

  // p->lock must be held when using these:
  enum procstate state;        // Process state
  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
  int trace_mask;              // trace syscall mas
...
};

这样就可以使用 myproc()->trace_mask = mask; 来存取mask值

为了在子线程里面把mask拷贝到子线程,所以,我们需要在fork()函数里把 trace_mask 拷贝给子线程。

// Create a new process, copying the parent.
// Sets up child kernel stack to return as if from fork() system call.
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;
  // 将 父线程的trace_mask给子线程
  np->trace_mask = p->trace_mask;
  .....
}
  1. 如何根据mask来打印跟踪的信息
  • 添加名字数组
const static char* syscall_names[] = {
[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]) {
    // Use num to lookup the system call function for num, call it,
    // and store its return value in p->trapframe->a0
    p->trapframe->a0 = syscalls[num]();
    // 通过 `&`运算来判断是否是目标进程
    // 比如 fork 的num 是 1 对应的二进制为
    // 1<<num:  00000000 00000000 00000000 00000000 00000001
    // mask  :  11111111 11111111 11111111 11111111 00000000
    // (mask) & (1 << num) = 00000000 00000000 00000000 00000000 00000000
    // 所以不输出
    if(p->trace_mask & (1 << num)) {
      printf("%d: syscall %s -> %d\n", p->pid, syscall_names[num],p->trapframe->a0);
    }
  } else {
    printf("%d %s: unknown sys call %d\n",
            p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

Sysinfo

添加一个 sysinfo 的系统调用来手机正在运行的系统的信息。输入参数: strcut sysinfo* pSysInfo。 内核需要填充这些字段。

  1. 添加系统调用
  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值