lab4 traps

105 阅读3分钟

这一个实验主要是加强你对系统调用,陷入内核的一些处理,突然发现lecture5的内容还是挺重要的,得重新回去看一下了

backtrace

做这个实验要知道一些前置知识

  • 这个课堂笔记中有张栈帧布局图。注意返回地址位于栈帧帧指针的固定偏移(-8)位置,并且保存的帧指针位于帧指针的固定偏移(-16)位置,一个栈分配一个PGSIZE

image.png

kernel/printf.c

void backtrace() {
  uint64 fp = r_fp();
  while(fp != PGROUNDUP(fp)) { // 如果已经到达栈底
    uint64 ra = *(uint64*)(fp - 8); // return address
    printf("%p\n", ra);
    fp = *(uint64*)(fp - 16); // previous fp
  }
}

alarm

根据hint,我们要完成一个sigalram,定时器每隔一些间隔执行一个处理函数,完成执行后执行一次sigreturn

完成这个有几个点要考虑

  • 如何完成在调用处理函数之前保存寄存器,以便处理完后继续顺利执行
  • 如何防止在一个处理函数未返回前,内核重复调用处理函数

完成第一个点

lecture6可知进程的寄存器被保存在struct trapframe中,由此可知我们只需要在proc结构体中再创建一个struct trapframe用于保存在调用前的trapfrme即可

完成第二个点

proc结构体中加一个标志,正在执行时设置为 1 ,结束时设置为 0 即可

由以上我们写出proc 结构体

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

  // p->lock must be held when using these:
  enum procstate state;        // Process state
  struct proc *parent;         // Parent process
  void *chan;                  // If non-zero, sleeping on chan
  int killed;                  // If non-zero, have been killed
  int xstate;                  // Exit status to be returned to parent's wait
  int pid;                     // Process ID
  uint64 handler;
  int ticks;
  int num;
  int in_handler;              // Is in process
  struct trapframe *clock_trapframe;
  // these are private to the process, so p->lock need not be held.
  uint64 kstack;               // Virtual address of kernel stack
  uint64 sz;                   // Size of process memory (bytes)
  pagetable_t pagetable;       // User page table
  struct trapframe *trapframe; // data page for trampoline.S
  struct context context;      // swtch() here to run process
  struct file *ofile[NOFILE];  // Open files
  struct inode *cwd;           // Current directory
  char name[16];               // Process name (debugging)
};

由hint写出 sigalarmsigreturn

uint64
sys_sigalarm(void){
  int ticks;
  argint(0, &ticks);

  uint64 handler;
  argaddr(1, &handler);

  struct proc* p =myproc();
  p->ticks = ticks;
  p->handler = handler;

  return 0;
}

uint64
sys_sigreturn(void){
  struct proc* p =myproc();
  
  *p->trapframe = *p->clock_trapframe;
  p->in_handler = 0;
  return 0;
}

由hint写出计时和调用

kernel/trap.c
  if(which_dev == 2){
    if(p->ticks != 0){
      p->num++;
      if(p->num == p->ticks && p->in_handler == 0){
        p->num = 0;
        *p->clock_trapframe = *p->trapframe;
        p->trapframe->epc = p->handler;
        p->in_handler = 1;
      }
    }
    yield();
    }

在进程初始化和释放进程中对新添加的字段进行处理

kernel/proc.c
static struct proc*
allocproc(void)
{
  struct proc *p;

  for(p = proc; p < &proc[NPROC]; p++) {
    acquire(&p->lock);
    if(p->state == UNUSED) {
      goto found;
    } else {
      release(&p->lock);
    }
  }
  return 0;

found:
  p->pid = allocpid();

  // Allocate a trapframe page.
  if((p->trapframe = (struct trapframe *)kalloc()) == 0){
    release(&p->lock);
    return 0;
  }

  if((p->clock_trapframe = (struct trapframe *)kalloc()) == 0){
    release(&p->lock);
    return 0;
  }

  // An empty user page table.
  p->pagetable = proc_pagetable(p);
  if(p->pagetable == 0){
    freeproc(p);
    release(&p->lock);
    return 0;
  }

  // Set up new context to start executing at forkret,
  // which returns to user space.
  memset(&p->context, 0, sizeof(p->context));
  p->context.ra = (uint64)forkret;
  p->context.sp = p->kstack + PGSIZE;
  p->num = 0;
  p->in_handler = 0;
  return p;
}

// free a proc structure and the data hanging from it,
// including user pages.
// p->lock must be held.
static void
freeproc(struct proc *p)
{
  if(p->trapframe)
    kfree((void*)p->trapframe);
  p->trapframe = 0;

    if(p->clock_trapframe)
    kfree((void*)p->clock_trapframe);
  p->clock_trapframe = 0;

  if(p->pagetable)
    proc_freepagetable(p->pagetable, p->sz);
  p->pagetable = 0;
  p->sz = 0;
  p->pid = 0;
  p->parent = 0;
  p->name[0] = 0;
  p->chan = 0;
  p->killed = 0;
  p->xstate = 0;
  p->state = UNUSED;
}