2021 MIT6.S081 LAB4 backtrace alarm

实验四


原网址 Lab: traps


RISCV-V assembly

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

    call.asm中地址0x24的指令将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.)
    

    可以看到都被编译器优化了,可以在编译期计算除结果,没有显示的调用。

  3. At what address is the function printf located?
    

    0x640

  4. What value is in the register ra just after the jalr to printf in main?
    

    盲猜一个30,因为jalr 1552(ra),ra+1552 = 0x640,1552 = 0x610,所以ra = 30

  5. Run the following code.
    
    	unsigned int i = 0x00646c72;
    	printf("H%x Wo%s", 57616, &i);
          
    What is the output? Here's an ASCII table that maps bytes to characters.
    The output depends on that fact that the RISC-V is little-endian. 
    If the RISC-V were instead big-endian what 
    would you set i to in order to yield the same output?
     Would you need to change 57616 to a different value?
    

    打印的是He110 World。如果RISC-V是小端系统,那么i理论上应该是0x726c6400,57616不变

  6. In the following code, what is going to be
     printed after 'y='? (note: the answer is not a specific value.)
      Why does this happen?
    
    	printf("x=%d y=%d", 3);
    

    寄存器a2的值是多少就打印多少。


Backtrace

添加一个功能,打印函数栈。

在这里插入图片描述

函数栈长这样,所以只需要获取sp和fp,由sp拿到栈底即可。

  1. riscv.h中加入内联汇编函数

    static inline uint64
    r_fp()
    {
      uint64 x;
      asm volatile("mv %0, s0" : "=r" (x) );
      return x;
    }
    
  2. printf.c中定义函数backtrace

    void backtrace(void)
    {
    	printf("backtrace:\n");
    	struct proc* p = myproc();
    	uint64 fp = r_fp();
    	while (fp > PGROUNDUP(p->kstack))
    	{
    		printf("%p\n", *(uint64*)(fp - 8));
    		fp = *(uint64*)(fp - 16);
    	}
    }
    
  3. sysproc.c中调用函数backtrace

    uint64 sys_sleep(void)
    {
        //...
        while(....) {
            if(...) {
                
            }
            backtrace();
            sleep();
        }
        //...
    }
    
  4. defs.h中声明backtrace函数的原型

    void backtrace(void);
    

    和GDB的backtrace(bt)打印出来的一样

    在这里插入图片描述

Alarm

添加一个系统调用sigalarm(interval,handler),interval是时间间隔,handler是你想要运行的函数的
函数名,每隔interval,就运行一次handler。

test0

  1. 添加两个系统调用,这里只给出用户的接口。

    /*		user/user.h		*/
    int sigalarm(int ticks, void (*handler)());
    int sigreturn(void);
    
  2. 结构体proc中加入新成员

    struct proc {
        //...
        void (*handle)(void);	//函数指针
        int ttr;				//time to run handle
    }
    
  3. 定义sys_sigalarm和sys_sigreturn函数

    /*		kernel/sysproc.c		*/
    uint64 sys_sigreturn(void)
    {
    	return 0;
    }
    
    uint64 sys_sigalarm(void)
    {
    	int time;
    	uint64 fn_addr;
    	if (argint(0,&time) < 0 || argaddr(1,&fn_addr) < 0)
    		return -1;
    	if (0 == time) 
    		return -1;
    	struct proc* p = myproc();
    	p->handle = (void (*)())fn_addr;
    	p->ttr = time;
    	return 0;
    }
    
  4. 修改usertrap()函数,使每隔一段时间运行handler。test0只需要修改epc寄存器就行

    /*		kernel/trap.c		*/
    void usertrap(void)
    {
        //...
        if (which_dev == 2)
        {
            static uint64 tick_cnt = 0;
            if (0 < p->ttr)
            {
              if (++tick_cnt == p->ttr))
              {
                  tick_cnt = 0;
                  p->trapframe->epc = (uint64)p->handle;
              }
            }
            else
              tick_cnt = 0;
            yield();
         }
        //...
    }
    
  5. 初始化proc中的ttr和handle

    /*		kernel/proc.c		*/
    static struct proc* allocproc(void)
    {
        //...
        p->ttr = 0;
        p->handle = 0;
        
        return p;
    }
    

test1 test2

test0只实现每隔一段时间运行handler,但是可能无法正确返回中断的地方继续运行。所以test1、test2就是实现sys_sigreturn,使之能正确返回。

要想正确返回的前提就是正确恢复中断前寄存器的值,但是具体是哪些寄存器,实验提示it will be many,这里偷了个懒,把所有寄存器的值都保存,没有一个个得寄存器去试。

  1. proc结构体添加新成员

    struct proc
    {
        //..
        struct trapframe prev;		//之前寄存器的状态
        int alarm_lock;				//在上一次handler返回之前,使之不能再次进入
    }
    

    初始化alarm_lock

    /*		kernel/proc.c		*/
    static struct proc* allocproc(void)
    {
        //...
        p->alarm_lock = 0;
        return p;
    }
    
  2. 修改sys_sigreturn,恢复进入handler之前寄存器的值,并使alarm_lock为0,即又可以进入handler了

    uint64 sys_sigreturn(void)
    {
    	struct proc* p = myproc();
    	*(p->trapframe) = p->prev;
    	p->alarm_lock = 0;
    	return 0;
    }
    
  3. 修改usertrap(),进入handler之前,保存所有寄存器,使alarm_lock = 1,即在调用sigreturn之前不能再次进入handler

    /*		kernel/trap.c		*/
    void usertrap(void)
    {
        //...
        if (which_dev == 2)
          {
            static uint64 tick_cnt = 0;
            if (0 < p->ttr)
            {
              if (0 == p->alarm_lock && ++tick_cnt == p->ttr))
              {
                tick_cnt = 0;  
                p->alarm_lock = 1;
                p->prev = *(p->trapframe);
                p->trapframe->epc = (uint64)p->handle;
              }
            }
            else
              tick_cnt = 0;
            yield();
          }
    }
    

    在qemu中运行usertests能通过,不知道为什么运行grade-lab-traps就不行。

    在这里插入图片描述

    在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值