前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >6.S081/6.828: 4 Lab traps

6.S081/6.828: 4 Lab traps

原创
作者头像
冰寒火
修改2022-11-26 05:01:35
4950
修改2022-11-26 05:01:35
举报
文章被收录于专栏:软件设计

这个实验探索系统调用是如何通过trap实现的,会涉及到汇编代码和寄存器操作,建议先参考xv6手册以及xv6源码分析--trap机制

一、RISC-V assembly

1 Which registers contain arguments to functions? For example, which register holds 13 in main's call to printf?

系统调用号保存在a7,参数保存在a0--a6,一般syscall会返回一个uint64放入到a0中,至于其他参数会在内核态直接写入用户指定的位置。printf中的参数13会被放在a2中。

2 Where is the call to function f in the assembly code for main? Where is the call to g? (Hint: the compiler may inline functions.)

汇编代码main函数中没有找到调用f的地方,应该是被内联取代函数调用了,直接设置为12。

image.png
image.png

3 At what address is the function printf located?

auipc rd, immediate意义是rd=immetiate<<12+pc。

代码语言:c
复制
void main(void) {
  1c:	1141                	addi	sp,sp,-16
  1e:	e406                	sd	ra,8(sp)
  20:	e022                	sd	s0,0(sp)
  22:	0800                	addi	s0,sp,16
  printf("%d %d\n", f(8)+1, 13);
  24:	4635                	li	a2,13
  26:	45b1                	li	a1,12
  
  28:	00000517          	auipc	a0,0x0
  2c:	7b050513          	addi	a0,a0,1968 # 7d8 <malloc+0xea>
//将0x0左移12位+pc值=pc值,
  30:	00000097          	auipc	ra,0x0
  34:	600080e7          	jalr	1536(ra) # 630 <printf>
  exit(0);
  38:	4501                	li	a0,0
  3a:	00000097          	auipc	ra,0x0
  3e:	27e080e7          	jalr	638(ra) # 2b8 <exit>

进入main函数时各个寄存器状态如下:

image.png
image.png
image.png
image.png

然后执行完auipc和addi后,寄存器状态:

image.png
image.png

最终jalr跳转到0x3ae位置,这就是printf syscall用户态地址。

image.png
image.png

5 In the following code, what is going to be printed after 'y='?

5221.

image.png
image.png

二、backtrace

1 问题分析

debugging时会有一个函数调用链,能够帮助我们发现在什么位置出现了什么error,实现这个功能,打印内核函数调用链。

  1. 通过stack frame pointer来打印调用链,gcc存储frame pointer在s0中。
  2. backtrace在kernel/printf.c中实现,并在sys_sleep中调用。
  3. xv6为每个进程分配了一页内核栈。
image.png
image.png

2 代码实现

代码语言:c
复制
void backtrace(void){
  
  printf("backtrace: \n");
  uint64 fp=r_fp();
  uint64 top=PGROUNDUP(fp);
  uint64 bottom=PGROUNDDOWN(fp);
  while (fp<top && fp>bottom){
    uint64 last_fp=*(uint64*)(fp-16);
    uint64 ra=*(uint64*)(fp-8);
    printf("%p\n",ra);
    fp=last_fp;
  }
}

三、alarm

1 问题分析

定时通知进程使用cpu的时间,这对一些需要限制cpu使用的进程有帮助。

  1. 增加系统调用sigalarm(interval, handler),每隔interval内核就调用一次handler。如果sigalarm(0, 0),内核就停止这个功能。
  2. interval、handler这两个应该存放到proc结构体中,并且还要追踪距离上次调用的时间间隔。
  3. cpu每隔一个tick,都会产生一个中断,在kernel/trap.c中由usertrap函数处理,会yield cpu,当前运行进程变为就绪态。

整个处理过程如图所示:

image.png
image.png
  1. 时钟中断(which_dev=2)时更新current_tick_num,一旦达到interval,就将用户态pc设置为alarm_handler,并设置in_handler=1。
  2. 如果in_handler=1就不能yield,而是应该立刻返回用户态,此时返回的位置是alarm_handler(periodic)。
  3. alarm_handler执行结束后通过sigreturn系统调用重新回到内核态,将原本的状态(alarmframe)赋值给trapframe,最重要的是用户态pc,设置为原本应该返回的位置,并将in_handler=0,下次中断就会yield该进程cpu。

2 代码实现

2.1 sigalarm

我们需要先在user 目录下添加系统调用声明代码,然后在kernel中添加系统调用实现,会从用户态传递两个参数。我们需要将这两个参数放入到proc结构体来维护,并且维护一个字段来追踪距离上次调用alarm间隔。

代码语言:c
复制
uint64 
sys_sigalarm(void){
  int ticks;
  uint64 handleraddr;
  if(argint(0, &ticks) < 0)
    return -1;
  if(argaddr(1, &handleraddr) < 0)
    return -1;
  struct proc *p=myproc();
  if(ticks==0||handleraddr==0){
    p->tick_interval=0;
    p->alarm_handler=0;
  }else{
    p->alarm_handler=(void (*)())handleraddr;
  }
  return 0;
}

//proc.c
struct proc {
  struct spinlock lock;

//...
  //for Lab 4,定时alarm
  int tick_interval; 
  void (*alarm_handler)(void);
  int current_tick_num;

 //...
};

每隔一个tick就会产生一个硬件时钟中断将进程yield出去,这个由usertrap处理,可以在这里判断是否需要执行进程的alarm_handler。alarm_handler是用户页表地址,内核页表没有对应页表项,所以需要切换到用户态去执行,结束后通过sigreturn进入到内核态。

代码语言:c
复制
void
usertrap(void)
{
  int which_dev = 0;

  //...
  
  if(r_scause() == 8){
    // system call

    //...
  } else if((which_dev = devintr()) != 0){
    // ok
    // printf("which_dev: %d, p->in_handler: %d\n",which_dev,p->in_handler);
    if(which_dev==2&&p->in_handler==0){
        p->current_tick_num++;
      //时钟中断时决定是否执行alarm_handler,并且不能够重入
      if(p->current_tick_num>=p->tick_interval &&  p->tick_interval!=0){
          acquire(&p->lock);
          p->in_handler=1;
          p->current_tick_num=0;
          p->alarmframe=*p->trapframe;
          //返回用户态,handler位置,执行完sigreturn进内核
          //此次回到用户态是为了执行handler
          p->trapframe->epc=p->alarm_handler;
          release(&p->lock);
      }
    }
  } else {
    printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
    printf("            sepc=%p stval=%p\n", r_sepc(), r_stval());
    p->killed = 1;
  }

  if(p->killed)
    exit(-1);

  // give up the CPU if this is a timer interrupt.
    
  if(which_dev == 2){
    if(!p->in_handler){
      //printf("pid: %d inhandler\n",p->pid);
      yield();
    }
      //如果in_handler=1,就直接返回用户态处理alarm_handler
  }

  usertrapret();
}



//sysproc.c

uint64 
sys_sigalarm(void){
  int ticks;
  uint64 handleraddr;
  if(argint(0, &ticks) < 0)
    return -1;
  if(argaddr(1, &handleraddr) < 0)
    return -1;
  struct proc *p=myproc();
  p->tick_interval=ticks;
  p->alarm_handler=handleraddr;
 
  // printf("sigalarm ,interval: %d, handler: %ld\n",p->tick_interval,p->alarm_handler);
  return 0;
}

uint64
sys_sigreturn(void){
  // printf("sigreturn ,\n");
  struct proc *p=myproc();
  acquire(&p->lock);
  *p->trapframe=p->alarmframe;
  p->in_handler=0;
  release(&p->lock);
  return 0;
}
image.png
image.png

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、RISC-V assembly
    • 1 Which registers contain arguments to functions? For example, which register holds 13 in main's call to printf?
      • 2 Where is the call to function f in the assembly code for main? Where is the call to g? (Hint: the compiler may inline functions.)
        • 3 At what address is the function printf located?
          • 5 In the following code, what is going to be printed after 'y='?
          • 二、backtrace
            • 1 问题分析
              • 2 代码实现
              • 三、alarm
                • 1 问题分析
                  • 2 代码实现
                    • 2.1 sigalarm
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档