Timer Interrupt 机制

定时器中断是 xv6 实现抢占式多任务的核心,是操作系统多个进程共享 CPU 的基础

流程: 中断发生 保存现场 中断处理 进程调度 上下文切换 恢复现场

在 xv6 操作系统中,定时器中断相关的源码如下:

1. 中断响应 kernel/trap.c void usertrap()

//
// handle an interrupt, exception, or system call from user space.
// called from trampoline.S
//
void
usertrap(void)
{
  int which_dev = 0;
// ...
  if(which_dev == 2) {
    yield();
  }
}

which_dev 为 2 时表示当前中断为 timer interrupt,调用 yield 函数让出 CPU

2. 让出CPU kernel/proc.c void yield()

// Give up the CPU for one scheduling round.
void
yield(void)
{
  struct proc *p = myproc();
  acquire(&p->lock);
  p->state = RUNNABLE;
  sched();
  release(&p->lock);
}

因为要修改进程属性,先给进程上锁,调用 sched 函数进行进程调度

kernel/proc.c void sched(void)

// Switch to scheduler.  Must hold only p->lock
// and have changed proc->state. Saves and restores
// intena because intena is a property of this
// kernel thread, not this CPU. It should
// be proc->intena and proc->noff, but that would
// break in the few places where a lock is held but
// there's no process.
void
sched(void)
{
  int intena;
  struct proc *p = myproc();
 
  if(!holding(&p->lock))
    panic("sched p->lock");
  if(mycpu()->noff != 1)
    panic("sched locks");
  if(p->state == RUNNING)
    panic("sched running");
  if(intr_get())
    panic("sched interruptible");
 
  intena = mycpu()->intena;
  swtch(&p->context, &mycpu()->context);
  mycpu()->intena = intena;
}

先做一轮状态合法性检查,然后等待调度器找到一个 RUNNABLE 状态的进程执行

kernel/proc.c void scheduler(void)

// Per-CPU process scheduler.
// Each CPU calls scheduler() after setting itself up.
// Scheduler never returns.  It loops, doing:
//  - choose a process to run.
//  - swtch to start running that process.
//  - eventually that process transfers control
//    via swtch back to the scheduler.
void
scheduler(void)
{
  struct proc *p;
  struct cpu *c = mycpu();
  
  c->proc = 0;
  for(;;){
    // Avoid deadlock by ensuring that devices can interrupt.
    intr_on();
    
    int found = 0;
    for(p = proc; p < &proc[NPROC]; p++) {
      acquire(&p->lock);
      if(p->state == RUNNABLE) {
        // Switch to chosen process.  It is the process's job
        // to release its lock and then reacquire it
        // before jumping back to us.
        p->state = RUNNING;
        c->proc = p;
        swtch(&c->context, &p->context);
 
        // Process is done running for now.
        // It should have changed its p->state before coming back.
        c->proc = 0;
 
        found = 1;
      }
      release(&p->lock);
    }
#if !defined (LAB_FS)
    if(found == 0) {
      intr_on();
      asm volatile("wfi");
    }
#else
    ;
#endif
  }
}