进程切换

  • 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