6.s081 lab 4

lab4

准备

 uservec做的事情:
     1.交换a0和 sscratch,然后a0就是TRAPFRAME了。(这里没懂,先记录下)
     2.将寄存器的值保存到trapframe中。
     3. 将a0的值(现在存在了sscratch中)也保存到trapframe
     4. 从trapframe中恢复内核的一些数据。(设置kernel pagetable等)
     5. 跳转到usertrap()

疑惑a0寄存器有啥用。
解答:在uservec执行前,所有的寄存器都保存了进程被中断时候的值。

    usertrap():
       1.首先看trap是不是来自user mode,然后将stvec设置为kernelvec。
       2. 保存当前进程的program counter。
       3. 判断是系统调用,设备中断,还是exception。然后采取相应措施。注意,如果是系统调用的话,program counter要加4,因为希望执行下一条指令。
       4. 调用usertrapret。
 
  usertrapret:
       1.首先将stvec设置为uservec
       2.设置trapframe里的一些参数
       3.设置ssstatus寄存器
       3.设置sepc寄存器
       4.调用userret
 
 userret:
     1.切换页表
     2.从trapfram中复原保存的寄存器的值,并设置sscratch为TRAPFRAME
     3.调用sret返回user mode

backtrace

void backtrace(void){
  uint64 st, ra,f,end;
  st = r_fp();
  end=PGROUNDUP(st);
  printf("backtrace:\n");
  while (st!=end)
  {
    ra = st - 8;
    f= st-16;
    printf("%p\n",*(uint64 *)ra);
    st=*(uint64 *)(f);
  }
  
}

alarm

这个实验包括两个系统调用sigalarm(interval, handler)和sigreturn( ),要我们每n个时间片调用一次handler函数。
  首先我们来通过test0。有关系统调用和proc.h的一些配置这里就不赘述了。这里只keenel/trap.c的代码。
void
usertrap(void)
{
  int which_dev = 0;

  if((r_sstatus() & SSTATUS_SPP) != 0)
    panic("usertrap: not from user mode");

  // send interrupts and exceptions to kerneltrap(),
  // since we're now in the kernel.
  w_stvec((uint64)kernelvec);

  struct proc *p = myproc();
  
  // save user program counter.
  p->trapframe->epc = r_sepc();
  
  if(r_scause() == 8){
    // system call

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

    // sepc points to the ecall instruction,
    // but we want to return to the next instruction.
    p->trapframe->epc += 4;

    // an interrupt will change sstatus &c registers,
    // so don't enable until done with those registers.
    intr_on();

    syscall();
  } else if((which_dev = devintr()) != 0){
    // ok
  } 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->ticks!=0){
      p->left--;
      if(p->left==0){
        //p->left=p->ticks;
        //p->ticks=0;
        //copy_trapframe(p->save_trapframe,p->trapframe);
       // memmove(p->save_trapframe,p->trapframe,sizeof(struct trapframe));
        p->trapframe->epc = (uint64)p->handler;
        
      }
        }

    yield();
  }
  usertrapret();
}

  第一次我做到这里的时候有个疑惑,既然在这里只改了epc,没有做别的处理。那么进程在跳转到hander函数后,怎么会把alarmtest剩余的部分执行完呢,是怎么跳回去的?

然后6.s081 lab 4_第1张图片
用gdb查看寄存器信息,pc调到handler函数,但是ra寄存器保存的还是原来指令执行的地址,handler执行完后,ra的值存入pc,然后程序继续执行。

  然后test1,和test2就是要保存切换到handler时候的寄存器信息,然后再调用sigreturn的时候复原。这个地方在proc结构中,增加个trapframe就可以了。开始的时候我自己写了个copy frame函数,后面发现直接用memmove函数就可以了,这是因为     p->trapframe和p->save_trapframe都是kalloc函数分配的,是物理地址,所以可以在内核直接用吗?
  然后还有要注意要free。

你可能感兴趣的:(单片机,嵌入式硬件)