进程调度
运行上下文
context:运行上下文包含的寄存器
// Saved registers for kernel context switches.
struct context {
uint64 ra;
uint64 sp;
// callee-saved
uint64 s0;
uint64 s1;
// ...
};
cpu:cpu基本信息,context 存储 CPU 运行 sheduler()
时上下文
// Per-CPU state
struct cpu {
struct proc *proc; // The process running on this cpu, or null.
struct context context; // swtch() here to enter scheduler().
};
proc:进程控制块,context 存储进程运行时上下文
// Per-process state
struct proc {
struct context context; // swtch() here to run process
};
swtch.S:交换运行上下文的汇编代码
# Context switch
# void swtch(struct context *old, struct context *new);
# Save current registers in old. Load from new.
.globl swtch
swtch:
sd ra, 0(a0)
sd sp, 8(a0)
sd s0, 16(a0)
sd s1, 24(a0)
# ...
ld ra, 0(a1)
ld sp, 8(a1)
ld s0, 16(a1)
ld s1, 24(a1)
# ...
ret
CPU 调度进程
CPU初始化完毕后,执行scheduler()
,循环遍历所有进程,如果有处于 RUNNABLE 状态的进程,调用swtch
保存CPU context,加载被调度进程 context,跳到进程context sp上次执行的位置
proc.c/scheduler()
// 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) {
for(;;) {
for(p = proc; p < &proc[NPROC]; p++) {
acquire(&p->lock);
if(p->state == RUNNABLE) {
p->state = RUNNING;
c->proc = p;
swtch(&c->context, &p->context);
c->proc = 0; // %%%%%%%
}
release(&p->lock);
}
}
}
进程让出CPU
当时间到达一定程度时,xv6 通过中断强迫处于用户态/内核态的应用程序陷入usertrap
/kerneltrap
,通过调用 yield()
放弃 CPU
void usertrap(void) {
if((which_dev = devintr()) != 0){
// ...
}
if(which_dev == 2) // give up the CPU if this is a timer interrupt.
yield();
}
void kerneltrap() {
// give up the CPU if this is a timer interrupt.
if(which_dev == 2 && myproc() != 0 && myproc()->state == RUNNING)
yield();
// the yield() may have caused some traps to occur,
// so restore trap registers for use by kernelvec.S's sepc instruction.
w_sepc(sepc);
w_sstatus(sstatus);
}
void yield(void) {
struct proc *p = myproc();
acquire(&p->lock);
p->state = RUNNABLE;
sched();
release(&p->lock);
}
yield()
执行 sched()
,调用 swtch
保存进程 context,加载 CPU context,跳到调度程序 schedule()
//%%%
处
proc.c/sched
void sched(void) {
// store register to process's context then load cpu context
swtch(&p->context, &mycpu()->context);
}
进程睡眠
sleep
// Atomically release lock and sleep on chan.
// Reacquires lock when awakened.
void sleep(void *chan, struct spinlock *lk) {
// Must acquire p->lock in order to change p->state and then call sched
// Once we hold p->lock, we can be guaranteed that we won't miss any wakeup (wakeup locks p->lock)
// so it's okay to release lk
acquire(&p->lock); //DOC: sleeplock1
release(lk);
p->chan = chan;
p->state = SLEEPING;
sched();
p->chan = 0;
release(&p->lock);
acquire(lk);
}