由于操作系统和编译器约定了 ABI,如下:
编译器在对 C 语言编译时,会自动 caller 标注的寄存器进行保存恢复。保存的步骤通常发生在进入函数的时候,恢复的步骤通常发生在从函数返回的时候。
内核线程切换需要保存的寄存器:通常仅保存 callee 标注的寄存器。原因:内核线程切换使用 swtch 函数。是在一个内核层的函数中间跳转到另一个内核层的函数中间。也就是说,程序不需要为 caller 标注的寄存器负责,因为调用 swtch 的函数已经把 caller 标注的寄存器保存好了(只要编译器是遵循上述 ABI 规范的)。
用户线程切换需要保存的寄存器:通常仅保存 caller 标注的寄存器。原因:用户线程切换通常是由于 sigalarm 进行切换。这个过程是从一个用户层函数的中间跳转到另一个用户层函数(handler)的开头。在跳转到另一个用户层函数的开头时,callee 标注的寄存器会被保存,并在从 handler 返回时恢复 callee 标注的寄存器。所以仅需保存 caller 标注的寄存器。
用户态和内核态之间的切换:caller 和 callee 都需要保存。原因:从用户层函数的中间跳转到内核层函数的中间,这期间没有经过任何“进入函数”和“从函数返回”,因此不会有任何寄存器被编译器生成的汇编自动保存。