Lec6 进程管理


进程调度

运行上下文

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

wakeup


文章作者: AthenaCrafter
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 AthenaCrafter !
  目录