进程切换

  • P1 进入内核,切换到调度器进程,调度器进程切换到 P2

  • 核心函数为 swtch()函数;该函数保存并加载部分寄存器的值(RISC-V 中存在调用者保存并恢复的寄存器(caller-saved registers),不需要保存全部寄存器)

切换过程

  • yield 调用了 sched 函数

  • sched 函数进行合理性检查,最后调用 swtch 函数交换当前进程的上下文和 CPU 调度线程的上下文,返回地址为切换后上下文的 ra 寄存器存的值;实际上是 scheduler 函数

    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;
    }
  • 调度器找到一个可运行的进程,再次运行 swtch 函数切换上下文, 此时的 ra 寄存器是之前是被定时器中断通过 sched 函数挂起的

    void
    scheduler(void)
    {
      struct proc *p;
      struct cpu *c = mycpu();
     
      c->proc = 0;
      for(;;){
        // The most recent process to run may have had interrupts
        // turned off; enable them to avoid a deadlock if all
        // processes are waiting.
        intr_on();
     
        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;
          }
          release(&p->lock);
        }
      }
    }
  • 第一次切换进程时,构造一个 forkret,allocproc 设置了 ra 和 sp 寄存器,forkret 本身只释放锁,调用 usertrapret