Mit6.s081lab4

预备知识:

陷阱(trap):

有三种事件会导致中央处理器搁置普通指令的执行,并强制将控制权转移到处理该事件的特殊代码上。一种情况是系统调用,当用户程序执行ecall指令要求内核为其做些什么时;另一种情况是异常:(用户或内核)指令做了一些非法的事情,例如除以零或使用无效的虚拟地址;第三种情况是设备中断,一个设备,例如当磁盘硬件完成读或写请求时,向系统表明它需要被关注。、

用户空间:

来自用户空间的陷阱的高级路径是uservec (kernel/trampoline.S:16),然后是usertrap (kernel/trap.c:37);返回时,先是usertrapret (kernel/trap.c:90),然后是userret (kernel/trampoline.S:16)。

系统调用流程:

用户调用是如何在内核中实现exec系统调用的。

用户代码将exec需要的参数放在寄存器a0a1中,并将系统调用号放在a7中。系统调用号与syscalls数组中的条目相匹配,syscalls数组是一个函数指针表(kernel/syscall.c:108)。ecall指令陷入(trap)到内核中,执行uservecusertrapsyscall,和我们之前看到的一样。

syscallkernel/syscall.c:133)从陷阱帧(trapframe)中保存的a7中检索系统调用号(p->trapframe->a7),并用它索引到syscalls中,对于第一次系统调用,a7中的内容是SYS_execkernel/syscall. h:8),导致了对系统调用接口函数sys_exec的调用。

当系统调用接口函数返回时,syscall将其返回值记录在p->trapframe->a0中。这将导致原始用户空间对exec()的调用返回该值,因为RISC-V上的C调用约定将返回值放在a0中。系统调用通常返回负数表示错误,返回零或正数表示成功。如果系统调用号无效,syscall打印错误并返回-1。

Backtrace:

void
backtrace(void) {
  printf("backtrace:\n");
  // 读取当前帧指针
  uint64 fp = r_fp();
  while (PGROUNDUP(fp) - PGROUNDDOWN(fp) == PGSIZE) {
    // 返回地址保存在-8偏移的位置
    uint64 ret_addr = *(uint64*)(fp - 8);
    printf("%p\n", ret_addr);
    // 前一个帧指针保存在-16偏移的位置
    fp = *(uint64*)(fp - 16);
  }
}

帧指针指向栈帧的栈顶,帧指针-8就是指向当前函数返回地址,-16就是指向上一个函数的帧指针。每个kernel stack都是一页,因此可以通过计算PGROUNDDOWN(fp)PGROUNDUP(fp)来确定当前的fp地址是否还位于这一页内,从而可以是否已经完成了所有嵌套的函数调用的backtrace。

Alarm:

实验要求:添加一个新的system callsigalarm(interval, handler)interval是一个计时器的tick的间隔大小,handler是指向一个函数的指针,这个函数是当计时器tick到达interval时触发的函数。

首先修改makefile:

然后在user/user.h中添加函数声明,系统调用用户接口

int sigalarm(int ticks, void (*handler)());
int sigreturn(void);

在user/user,pl中添加 entry入口

在kernel/syscall.h中添加 系统调用号

在kernel/syscall.c中添加 注册系统调用

在kernel/proc.h中添加结构体成员

  int interval;                //定时器出发周期
  void (*handler)();           // 指向函数得指针
  int ticks;                   // 计时器
  int in_handler;              //记录是否还在handler函数中
  uint64 saved_epc;
  uint64 saved_ra;
  uint64 saved_sp;
  uint64 saved_gp;
  uint64 saved_tp;
  uint64 saved_t0;
  uint64 saved_t1; 
  uint64 saved_t2;
  uint64 saved_s0;
  uint64 saved_s1;
  uint64 saved_s2;
  uint64 saved_s3;
  uint64 saved_s4;
  uint64 saved_s5;
  uint64 saved_s6;
  uint64 saved_s7;
  uint64 saved_s8;
  uint64 saved_s9;
  uint64 saved_s10;
  uint64 saved_s11;
  uint64 saved_a0;
  uint64 saved_a1;
  uint64 saved_a2;
  uint64 saved_a3;
  uint64 saved_a4;
  uint64 saved_a5;
  uint64 saved_a6;
  uint64 saved_a7;
  uint64 saved_t3;
  uint64 saved_t4;
  uint64 saved_t5;
  uint64 saved_t6;

在kernel/sysfile.c中实现具体函数

sys_sigalarm将userfunction传入得两个参数分别保存到p结构体相应的成员变量中

uint64
sys_sigalarm(void)
{
  int ticks;
  uint64 funaddr; // pointer to function
  if ((argint(0, &ticks) < 0) || (argaddr(1, &funaddr) < 0)) {
    return -1;
  }
  struct proc *p = myproc();
  p->interval = ticks;
  p->handler = (void(*)())funaddr;
  return 0;
}

sys_sigreturn会在handler函数完成后被调用,注意工作就是恢复现场得寄存器,in_handler置09

uint64
sys_sigreturn(void)
{
  struct proc *p = myproc();
  p->trapframe->epc = p->saved_epc;
  p->trapframe->ra = p->saved_ra;
  p->trapframe->sp = p->saved_sp;
  p->trapframe->gp = p->saved_gp;
  p->trapframe->tp = p->saved_tp;
  p->trapframe->t0 = p->saved_t0;
  p->trapframe->t1 = p->saved_t1;
  p->trapframe->t2 = p->saved_t2;
  p->trapframe->t3 = p->saved_t3;
  p->trapframe->t4 = p->saved_t4;
  p->trapframe->t5 = p->saved_t5;
  p->trapframe->t6 = p->saved_t6;
  p->trapframe->s0 = p->saved_s0;
  p->trapframe->s1 = p->saved_s1;
  p->trapframe->s2 = p->saved_s2;
  p->trapframe->s3 = p->saved_s3;
  p->trapframe->s4 = p->saved_s4;
  p->trapframe->s5 = p->saved_s5;
  p->trapframe->s6 = p->saved_s6;
  p->trapframe->s7 = p->saved_s7;
  p->trapframe->s8 = p->saved_s8;
  p->trapframe->s9 = p->saved_s9;
  p->trapframe->s10 = p->saved_s10;
  p->trapframe->s11 = p->saved_s11;
  p->trapframe->a0 = p->saved_a0;
  p->trapframe->a1 = p->saved_a1;
  p->trapframe->a2 = p->saved_a2;
  p->trapframe->a3 = p->saved_a3;
  p->trapframe->a4 = p->saved_a4;
  p->trapframe->a5 = p->saved_a5;
  p->trapframe->a6 = p->saved_a6;
  p->trapframe->a7 = p->saved_a7;
  p->in_handler = 0;
  return 0;
}

修改kernel/trap.c,usertrap()是对中断定时器处理,当p->ticks到达预定值p->interval,将调用p->handler,这是通过向p->trapframe->epc加载handler地址实现的,这样当从usertrap返回时,pc恢复现场时会加载epc,就会跳转到handler地址,跳转前保存好寄存器。

else if((which_dev = devintr()) != 0){
    // ok
    if (which_dev == 2 && p->in_handler == 0) {
      p->ticks += 1;
      if ((p->ticks == p->interval) && (p->interval != 0)) {
        p->in_handler = 1;
        p->ticks = 0;
        p->saved_epc = p->trapframe->epc;
        p->saved_ra = p->trapframe->ra;
        p->saved_sp = p->trapframe->sp;
        p->saved_gp = p->trapframe->gp;
        p->saved_tp = p->trapframe->tp;
        p->saved_t0 = p->trapframe->t0;
        p->saved_t1 = p->trapframe->t1;
        p->saved_t2 = p->trapframe->t2;
        p->saved_t3 = p->trapframe->t3;
        p->saved_t4 = p->trapframe->t4;
        p->saved_t5 = p->trapframe->t5;
        p->saved_t6 = p->trapframe->t6;
        p->saved_s0 = p->trapframe->s0;
        p->saved_s1 = p->trapframe->s1;
        p->saved_s2 = p->trapframe->s2;
        p->saved_s3 = p->trapframe->s3;
        p->saved_s4 = p->trapframe->s4;
        p->saved_s5 = p->trapframe->s5;
        p->saved_s6 = p->trapframe->s6;
        p->saved_s7 = p->trapframe->s7;
        p->saved_s8 = p->trapframe->s8;
        p->saved_s9 = p->trapframe->s9;
        p->saved_s10 = p->trapframe->s10;
        p->saved_s11 = p->trapframe->s11;
        p->saved_a0 = p->trapframe->a0;
        p->saved_a1 = p->trapframe->a1;
        p->saved_a2 = p->trapframe->a2;
        p->saved_a3 = p->trapframe->a3;
        p->saved_a4 = p->trapframe->a4;
        p->saved_a5 = p->trapframe->a5;
        p->saved_a6 = p->trapframe->a6;
        p->saved_a7 = p->trapframe->a7;
        p->trapframe->epc = (uint64)p->handler;
      }
    }
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值