Skip to Content

调度

时钟中断和进程切换,这两个过程中寄存器的保存/恢复有什么不同?

这个问题是来自于《操作系统导论》6.3 节。

请注意,在此协议中,有两种类型的寄存器保存/恢复。第一种是发生时钟中断的时候。在这种情况下,运行进程的用户寄存器由硬件隐式保存,使用该进程的内核栈。第二种是当操作系统决定从A切换到B。在这种情况下,内核寄存器被软件(即OS)明确地保存,但这次被存储在该进程的进程结构的内存中。后一个操作让系统从好像刚刚由A陷入内核,变成好像刚刚由B陷入内核。

每个进程的内核栈

在理解两种保存/恢复方式前,需要先知道:每个进程在内核都有一个独立的内核栈(通常大小固定,如 8KB)。当进程在用户态运行时,一旦发生系统调用、中断或者异常,就会陷入内核态,此时 CPU 会自动切换到该进程的内核栈。

  • 这个内核栈是进程在内核态工作的“临时空间”。
  • 当进程在内核态时,用的就是它自己的那块栈空间;别的进程不会用、也不会“踩”进去。

内核栈只能暂时存放本次陷入内核时需要的信息,不用于长期保存上下文(它更像一个临时工作区)。而进程的长期上下文信息(寄存器、PC 等)最终会保存在进程控制块(PCB)中。


时钟中断:硬件自动保存寄存器

第一种情况是时钟中断(或其他中断)发生时。此时假设当前进程正在用户态运行,那么一旦中断触发:

  1. CPU 自动地将进程在用户态的一些寄存器(包含程序计数器 PC、栈指针 SP 等)压到该进程的内核栈里。
  2. CPU 切换到内核态后,进入中断处理程序。操作系统可以检查是否需要调度别的进程。

这里的“用户寄存器”保存是由硬件自动完成的,保存位置是“运行中进程”的内核栈。

“中断发生时,CPU 和硬件自动地将进程的上下文等保存到内核栈。”


进程切换:OS 软件显式保存寄存器

第二种情况是在 CPU 已经运行在内核态的情况下,操作系统决定从进程 A 切换到进程 B。这时的上下文保存/恢复方式与上述中断时的硬件自动保存不同。

  1. 保存当前进程 A 的内核态上下文

    • OS 会将 A 已经压在内核栈上的寄存器信息,以及内核态的其他关键状态,拷贝到 A 的 PCB(进程控制块)中。
    • 这意味着,CPU 原本在内核态时的各种寄存器(如 eax、ebx、栈指针、程序计数器等)都被保存起来,确保下次 A 被重新调度时能恢复。
  2. 加载进程 B 的上下文

    • 从 B 的 PCB 中取出它上次运行时保存的寄存器值,恢复到 CPU 中。
    • 这样从 CPU 角度看,就好像 B 一直在内核态,刚刚被中断进来的一系列栈内容都是 B 的。

原文:“后一个操作让系统从好像刚刚由 A 陷入内核,变成好像刚刚由 B 陷入内核。”

也就是说,通过在内核态就地“转换”寄存器和栈等信息,CPU 就会“以为”自己是从 B 的上下文切入内核,然后再继续执行 B(也可能马上返回用户态,执行 B 的用户代码)。这就是上下文切换的“魔术”所在:

“从 CPU 的角度,它只关心寄存器和内存里的值,操作系统只要把这些值改成另一个进程的,它就无感知地切换了上下文。”


为什么必须把寄存器状态存到 PCB?

因为内核栈是临时的,一旦处理完本次中断或系统调用,后续要返回用户态时,堆栈里的内容往往就被弹出、清空。要想以后还能恢复到某个进程上次执行到哪儿,就必须把这些关键状态(寄存器、PC 等)保存到 PCB 这种“持久”位置。

“一段内核态执行结束后,通常就会把涉及寄存器 / 关键状态的信息保存到 PCB。内核栈只负责本次内核执行用完即释放,不保留持久状态。PCB才是‘进程真正的上下文存储地’。”


上下文切换的典型流程

总结一下一个常见的调度或切换过程:

  1. 时钟中断或系统调用 使得 CPU 从用户态切到内核态(此时在 A 的内核栈)。
  2. 操作系统的调度器判断:“我要切换到进程 B”。
  3. 此时并不会把 A 再送回用户态(那还要额外地走一遍内核返回流程),而是直接在内核态就地切换:
    • 把 A 当前在内核栈中压着的寄存器信息整理拷贝到 A 的 PCB,封存了此刻 A 的内核态上下文。
    • 从 B 的 PCB 中加载 B 的内核态上下文(它之前也保存在 PCB 里)。
  4. 最后,从内核态返回到 B 的用户态(或继续执行 B 在内核态的一些操作)。对 CPU 来说,寄存器和栈都变成 B 的了,等同于“刚刚就是 B 陷入的内核”。

用户态 vs. 内核态 被中断时上下文的区别

从 PCB 的角度来看,里面保存的可能是进程在用户态的寄存器信息,也可能是它在内核态的寄存器信息。常见场景如下:

  1. B 在用户态运行时被打断(最常见)
    • 正在跑用户代码,时钟中断来了。
    • 被打入内核态时,CPU 把它的用户态寄存器保存到了 B 自己的内核栈上。
    • OS 决定切换到其他进程,就把 B 的内核栈上那部分状态拷到 B 的 PCB。
    • 下次再调度到 B 时,就从 PCB 中取出这些状态,i-ret 返回用户态,让 B 继续执行被中断的用户代码。
  2. B 在内核态运行时被打断(少见)
    • B 可能在执行系统调用(还在内核中),还没返回用户态就因为时间片到了要被切走。
    • 这时保存到 PCB 的,是 B 当时在内核态的一些寄存器、PC 等信息。
    • 将来恢复 B 时,会让它先继续执行内核态的那部分代码,然后再返回到用户态。

不过,大多数操作系统会尽量避免进程在内核态被换出。通常会等系统调用结束、即将返回用户态前,再进行进程切换。这样只需要保存/恢复用户态上下文即可,简化了管理。


小结:

  • 时钟中断时:硬件自动把进程的用户寄存器压到它的内核栈;
  • 进程切换时:操作系统在内核态显式地把寄存器状态(可能是内核态也可能是用户态)拷到进程 PCB 中,再加载目标进程的 PCB;
  • 内核栈:临时工作区,本进程专属,用完不做持久化;
  • PCB:持久保存当前进程的上下文(含寄存器、PC、内核/用户态信息),便于随时恢复。

这也说明了“让系统从好像刚刚由 A 陷入内核,变成好像刚刚由 B 陷入内核”的本质:只要改对了寄存器和堆栈,CPU 就不会察觉自己其实换了个进程。


“A 在内核态被暂停,B 在内核态被恢复,然后再回到 B 的用户态。”

这句话正是上述过程的最简洁描述:操作系统本质上是在内核态完成了保存 A、加载 B 的一系列动作,CPU 对此无感知。只要保存和恢复的寄存器信息正确,下一次“返回”就会回到 B 的世界里。


与 ChatGPT 的对话

Last updated on