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
}
}