【问题标题】:How can a CPU save its register state in a context switch?CPU 如何在上下文切换中保存其寄存器状态?
【发布时间】:2018-03-30 20:34:41
【问题描述】:

我一直在学习操作系统课程,在关于上下文切换的讲座中,提到当发生上下文切换时,操作系统会将所有寄存器的状态保存到一个 PCB 块中,以便当操作系统选择重新访问它时,可以恢复此过程的确切状态。

在汇编中,有人会如何编写代码来保存所有这些信息?据我了解,如果您想将任何信息写入内存,则需要将内存位置存储在您的一个寄存器中。因此,在将寄存器写入内存的过程中,至少有一个寄存器必须被覆盖到 PCB 块中的某个位置,并且存储在该寄存器中的信息将会丢失。

是否有硬件支持使这项任务成为可能?

【问题讨论】:

  • 这个问题的答案很大程度上取决于您正在编程的架构。一些架构(如 x86)具有硬件支持,而其他架构(如 SPARC)则没有。此外,操作系统之间的实现方式不同,因此请至少将问题缩小到单个 CPU。
  • 好的,谢谢。在我完成课程的过程中,我真的只是对这个问题感到好奇,我想知道这样的事情怎么可能发生。我目前没有开发任何需要能够做到这一点的东西,我唯一熟悉的架构是 MIPS,但我还没有深入研究过它

标签: assembly operating-system context-switch


【解决方案1】:

下面是一个例子:
1. 将一个寄存器保存到堆栈。
2. 使用 PCB 的地址加载该寄存器。
3. 保存PCB中的所有状态,包括获取栈中保存的寄存器值。

【讨论】:

  • 其他可以使用的技巧:使用每个 CPU absolute 地址,这样您就可以在没有寄存器中的指针的情况下写入它。 (例如,在 x86-64 上,swapgsgs 段寄存器与额外的内部寄存器交换允许您使用mov [gs: 16], rsp 或其他任何东西来保存用户空间的堆栈指针,然后再将 RSP 设置为指向内核堆栈,例如使用来自[gs:0] 的负载)。或者 CPU 具有可以帮助您的功能,例如 ARM 的寄存器组,它为中断处理程序提供一组与用户空间不同的寄存器。
  • 考虑到上下文切换只会发生一次(它不是可重入的,因为下一次上下文切换距离 N [微]秒),您可以将一二寄存器存储到一些绝对内存中,然后使用它们将 CPU 的全部剩余状态保存在某处,然后从绝对内存中加载这 1-2 个值并将它们添加到该状态以使其完整(如果您不想破坏用户堆栈和您的中断机制CPU 没有像 x86_64 那样的单独的用户/中断堆栈机制,但相反有绝对内存缓冲区是可能的)。
  • @Ped7g:如果在此期间出现网卡或声卡中断,触发唤醒一个比您切换到的优先级更高的实时进程怎么办?使用可抢占式内核(如现代 Linux),很明显,您最终不能将保存过程中的状态视为您希望稍后恢复的旧上下文。您可能需要在禁用中断的情况下运行这部分状态保存,以便您可以安全地使用绝对内存位置。 (Linux 使用每线程内核堆栈的底部作为用户空间状态,但 IDK 用于内核抢占)
  • (顺便说一句,这种绝对定位技巧在现代操作系统中用于保存从用户空间进入内核时发生的所有代码。内核内部的__switch_to 看起来像一个函数(称为来自sched()),所以调用者已经期望它破坏一些注册,因此它有一些临时注册可以玩。How does schedule()+switch_to() functions from linux kernel actually work?github.com/torvalds/linux/blob/master/arch/x86/entry/…
【解决方案2】:

一般来说,处理器定义一个进程上下文块 (PCB)。这是一种数据结构,处理器在其中存储和加载寄存器。在大多数情况下,操作系统并不需要知道 PCB 的内部结构,只需要知道尺寸即可。

处理器通常具有加载进程上下文和保存进程上下文指令。为简单起见,假设您有带有标签 PROCESS_1 和 PROCESS_2 的进程上下文块,其中前者是当前正在运行的进程,要切换进程,您可以执行以下操作:

 SVPCTX  PROCESS_1
 LDPCTX  PROCESS_2 ; As soon as this instruction execute the context switch is complete.

这在某些具有多个寄存器集的系统上可能会更复杂(比如“Hi”Intel),但我所描述的是它通常如何在大多数处理器上工作。

【讨论】:

  • 所以您是在建议您可以对 PCB 使用静态存储?如果您有一些预定义的上下文,并且新/旧上下文是汇编时常量,则此方法有效。这不能扩展到任意数量的进程。
  • 不,我不是。请注意“为简单起见”限定符。实际上,有一些基于寄存器的寻址。到动态数据结构。
  • 那么在用指向 PCB 的指针破坏它之前如何保存该寄存器?答案是两步的组合,将所有用户空间上下文保存在进入内核(系统调用或中断入口点)的已知位置,就像@prl的答案一样,然后使上下文切换成为函数调用不必保存/恢复所有寄存器。
  • 你不保存指向PCB的寄存器。一旦您执行加载流程上下文,您就处于新流程中并且不在乎。
  • 但是您最终需要能够返回到保存的上下文,因此它必须包含整个用户空间架构状态。当您第一次进入内核时,所有寄存器都保持用户空间状态,您需要避免损坏,您可以如何/在哪里保存任何内容并不明显。需要小心使用静态位置,以免被破坏。我认为这就是 OP 所要求的本质。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-03
  • 2015-09-25
相关资源
最近更新 更多