我无法理解 xv6 中的这行代码

I can't understand this line of code in xv6

尽管查阅了文档,我仍然无法理解这一行:swtch(&c->scheduler, &p->context);

我的问题:我知道这一行是切换p->context,包括save registers和restore registers,但是我看不懂这个过程中pc的变化,就是什么执行顺序这个代码? swtch(&c->scheduler, &p->context);执行完后,是不是马上执行c->proc = 0;,那么执行c->proc=p; 的效果就消失了?我现在很困惑。

代码link:https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/proc.c 第 456 行


*// 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->scheduler, &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(found == 0){
      intr_on();
      asm volatile("wfi");
    }
  }
}

从字面上看,这是一段令人困惑的代码,其原始作者在评论中写道“你不应该理解这个”,所以不要因为不理解它而难过。

您可能错过的关键是 p->context 包含一个地址,其中 swtch 用于恢复进程 p 的执行。例如,它的设置是 here:

  // Set up new context to start executing at forkret,
  // which returns to user space.
  memset(&p->context, 0, sizeof(p->context));
  p->context.ra = (uint64)forkret;
  p->context.sp = p->kstack + PGSIZE;

因此,当调度程序调用 swtch 时,它实际上是在对 p->context.ra 指向的任何内容进行间接函数调用。该代码将无限期执行,然后最终(有点)return 到 swtch,return 到调度程序,调度程序继续 c->proc = 0

(在上面的句子中,“有点”和“有效”这两个词做了很多工作。要了解隐藏在这些词后面的是什么,接下来你应该阅读的是 coroutines .)