我已经读过 setjmp 在传入的 jmp_buf 变量中“保存程序状态”,但我还没有找到任何关于它的确切含义的描述。它是否会复制所有应用程序的内存?只是寄存器?堆栈?
4 回答
以下内容来自Peter Prinz 和 Tony Crawford 的C in a Nutshell:
宏将
setjmp()
调用时的当前环境保存在其参数指定的缓冲区中。环境包括堆栈,以及所有具有自动存储持续时间的变量。
以下是ISO/IEC 9899:TC2在第 7.13 节中所说的内容:
调用
setjmp
宏的环境包含足以调用longjmp
函数以将执行返回到正确块和调用该块的信息,如果它被递归调用的话。它不包括浮点状态标志、打开文件或抽象机的任何其他组件的状态。
这是 PJ Plauger 在他的书《标准 C 库》中的一个有趣的参考:
[实现 setjmp] 的危险之一在于表达式评估。典型的计算机有一些寄存器,用于在计算表达式时保存中间结果。但是,编写一个足够复杂的表达式,您可能会耗尽可用的寄存器......必须猜测要在数据对象
setjmp
中存储多少“调用上下文” 。jmp_buf
可以肯定的是,必须保存某些寄存器。
最后,来自Peter Van Der Linden 的Expert C Programming。
Setjmp 将程序计数器的副本和当前指针保存到堆栈顶部。
根据上述信息,在我看来,“当前环境”是由实施决定的。
根据平台 ABI,它只是需要在函数调用中保留的寄存器。
来源:在各种操作系统上反汇编x86、x64、arm32、arm64上的setjmp。
setjmp() 函数保存大多数通用寄存器的内容,就像它们保存在任何函数条目中一样。它还保存堆栈指针和返回地址。所有这些都放在缓冲区中。然后它安排函数返回零。
longjmp() 函数恢复通用寄存器和堆栈指针,然后跳转到先前保存的返回地址。在实践中,它可以显式地执行此操作,或者通过设置堆栈并执行正常的函数返回来执行此操作。在这种情况下,该函数返回一个非零值。
原理是一样的,但是在我遇到的许多不同的 CPU 中,细节差异很大。
从 setjmp 手册页
setjmp() and longjmp(3) are useful for dealing with errors and interrupts encountered in a
low-level subroutine of a program. setjmp() saves the stack context/environment in env for
later use by longjmp(3). The stack context will be invalidated if the function which called
setjmp() returns.
本质上,它会记住当前堆栈位置和寄存器状态。当您调用 longjmp 时,您会跳回到相同的程序计数器和堆栈位置,并恢复一些额外的寄存器。
这些通常被称为“非本地 goto”。它们不像会复制内存状态或类似的东西的叉子。