6.S081 lab4: traps

1. 写在前面

  感觉这个lab给我最大的收获就是:不加参数的cat,用ctrl+D退出(笑)。之前用cat的时候忘了写参数然后就陷入cat的循环里去了,不知道怎么退出,就只好强行关掉shell。另外,addr2line这个指令挺有用的,用于把地址翻译到相应函数。记录一下

2. lab任务流程

  有三个任务:回答汇编问题,写一个backtrace(虽然我感觉毫无帮助),外加一个alarm调用实现。

3. backtrace实现

  这个backtrace也算有点收获,认识到了s0寄存器在函数调用约定中的作用,s0作为帧指针,指向该函数栈帧的第一个字节(虽然我觉得如果优化级别开高一点gcc就不会使用帧指针了?)。

void
backtrace(){
	uint64 s0 = r_fp(); 
	uint64 up_addr = PGROUNDDOWN(s0) + PGSIZE;
	//s0 = *(uint64*)(s0 - 16);
	printf("backtrace\n");
	while(s0 < up_addr){
		printf("%p\n", *(uint64*)(s0 - 8)); //获取该函数的返回地址ra
		s0 = *(uint64*)(s0 - 16);  //获取上一个函数的帧指针
	}
}

  r_fp()就是一个读取s0的嵌入汇编。

4. alarm实现

  具体要求是提供这样一种对外接口:

uint64 sigalarm(interval, handler);
uint64 sigreturn();

  用户调用系统调用signalarm后,要求每经过interval个时钟中断后,调用handler函数,但是要求用户在handler函数的最后必须调用sigreturn来进行返回。且sigalarm(0, 0)语义是取消alarm。
  在proc结构中添加如下属性:
在这里插入图片描述
  interval和handler用于记录用户传入的参数。acc是计数器,当acc>=interval时就调用用户的handler函数。在allocproc中将inerval和handler初始为0,而将acc初始为-1(因为我希望在interval为0时表示用户没有调用闹钟)
  因此sigalarm的实现如下:

uint64
sys_sigalarm(){
	int interval;
	uint64 handler;
	struct proc* p = myproc();
	if(argint(0, &interval) < 0){
		return -1;
	}
	if(argaddr(1, &handler) < 0){
		return -1;
	}
	p->interval = interval;
	p->handler = (void*)handler;
	if(p->interval == 0)p->acc = -1;
	else p->acc = 0;
	return 0;
}
uint64
sys_sigreturn(){
	struct proc* p = myproc();
	p->sign = 1;
	return 0;
}	

  sigreturn只是简单地设置一个标志位sign,在usertrapret函数中通过这个标志位来判断用户是否是调用sigreturn而陷入内核。
  我们在处理时钟中断的时候进行acc的更新:
在这里插入图片描述

  usertrapret函数的修改如下:

// send syscalls, interrupts, and exceptions to trampoline.S
  w_stvec(TRAMPOLINE + (uservec - trampoline));

  if(p->sign == 1){  //第一部分的增加
	  p->sign = 0;
	  p->in_handle = 0;
	  //if(p->interval == 0)p->acc = -1;
	  //else p->acc = 0;
	  memmove(p->trapframe, &(p->restore), sizeof(struct trapframe));

  }

  // set up trapframe values that uservec will need when
  // the process next re-enters the kernel.
  p->trapframe->kernel_satp = r_satp();         // kernel page table
  p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack
  p->trapframe->kernel_trap = (uint64)usertrap;
  p->trapframe->kernel_hartid = r_tp();         // hartid for cpuid()

  // set up the registers that trampoline.S's sret will use
  // to get to user space.
  
  // set S Previous Privilege mode to User.
  unsigned long x = r_sstatus();
  x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode
  x |= SSTATUS_SPIE; // enable interrupts in user mode
  w_sstatus(x);

  // set S Exception Program Counter to the saved user pc.
  if(p->acc >= p->interval && p->in_handle == 0){ //第二部分增加
	 w_sepc((uint64)p->handler);
	 memmove(&(p->restore), p->trapframe, sizeof(struct trapframe));
	 //printf("现在acc:%d,现在interval: %d\n", p->acc, p->interval);
	 //p->acc = 0;
	 p->acc = 0;
	 p->in_handle = 1; 
  } 
  else{	
	  //printf("我进入了这里\n");
	  w_sepc(p->trapframe->epc);
  }

  // tell trampoline.S the user page table to switch to.
  uint64 satp = MAKE_SATP(p->pagetable);

  可以看到,主要增加了两个if判断的语句。首先看第二部分的增加,这部分用来判断是否需要进入handler函数。in_handle标识位的作用在于判断该进程是否处于handler(防止hanlder的重入)。如果acc超过了interval,则设置返回pc为handler,同时保存进程的trapframe。然后把acc清0同时设置in_handle为1(表明进程接下来会处于handler)。
  第一部分主要做handler的退出工作,设置in_handler=0,然后恢复进程原来的trapframe(当然接下来不一定能回到进程原来的位置,因为有可能handler的执行时间超过interval个时钟中断,进程又回到handler中)。
  主要的工作差不多就这些了吧。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值