我正在运行Ubuntu 9.10(Karmic Koala),我看了一下jmp_buf
结构,它只是一个12个整数的数组.当我使用setjmp
并传入jmp_buf
结构时,12个条目中的4个被保存.这4个条目是堆栈指针,帧指针,程序计数器和返回地址.其他8个条目是什么?它们是机器相关的吗?段表基址寄存器的另一个条目是?还有什么需要正确恢复线程/进程的环境?我查看了手册页,其他来源,但我找不到汇编代码setjmp
.
我通常不会编写C++代码,但我的一个奇怪的comp sci朋友厌倦了查看我精彩的FORTRAN程序并挑战我用C++重写其中一个,因为他更喜欢我的C++代码.(我们在这里投入资金.)确切的术语是它需要在现代C++编译器中进行编译.也许他讨厌一个好的conio.h - 我不知道.
现在我意识到在C++中有很好的写作方式,但是我想通过尽可能使我的C++版本成为FORTRAN-esque来获得个人胜利.对于奖励积分,当我转换代码时,这可能会节省一些时间和精力.
所以!这带我进入以下相关查询:
在gotos:
在longjmp上:
现在我的主要关注点是为此计算goto.看起来我可能会使用longjmp来完成这项工作,因为void指针数组不是C++标准的一部分,而是GCC特定的扩展.
在此问答中,您应该始终致电va_end()
:
但是如果你到达va_end之前有一段代码longjmp呢?va_end的部分是否有任何承诺可以接受?或者从概念上讲(例如)va_start()
可能会泄漏内存分配,而不仅仅是使用堆栈技巧?
如果我放在atexit( fn );
退出堆栈上,它将在程序退出时执行:从main()
或从中返回exit()
.
我可以从堆栈中删除它吗?
你问我为什么要这样做?
我正在尝试使用简单的try-catch机制atexit
,setjmp
和longjmp
.如果可以的undo-atexit(fn);
话,这将是完美的- 即使它只适用于最后注册的功能.
编辑:
在monoceres建议我自己的堆栈...
堆栈现在仅适用于一个异常捕获器.
void (*_catchFn[10])() = {0,0,0,0,0,0,0,0,0,0};
void _catch(){
if ( _catchFn[0] != 0 ){
(_catchFn[0])();
}
}
void _addCatch( void (*fn)() ){
_catchFn[0]=fn;
}
void _remCatch( void (*fn)() ){
_catchFn[0]=0;
}
void test(){
jmp_buf env;
void catch(){ // we get here after an exit with a registered catch
longjmp(env,1); // return to the line marked except...
// that …
Run Code Online (Sandbox Code Playgroud) 所以我有一个库(不是我写的),不幸的是它abort()
用来处理某些错误.在应用程序级别,这些错误是可恢复的,所以我想处理它们而不是用户看到崩溃.所以我最终编写这样的代码:
static jmp_buf abort_buffer;
static void abort_handler(int) {
longjmp(abort_buffer, 1); // perhaps siglongjmp if available..
}
int function(int x, int y) {
struct sigaction new_sa;
struct sigaction old_sa;
sigemptyset(&new_sa.sa_mask);
new_sa.sa_handler = abort_handler;
sigaction(SIGABRT, &new_sa, &old_sa);
if(setjmp(abort_buffer)) {
sigaction(SIGABRT, &old_sa, 0);
return -1
}
// attempt to do some work here
int result = f(x, y); // may call abort!
sigaction(SIGABRT, &old_sa, 0);
return result;
}
Run Code Online (Sandbox Code Playgroud)
代码不是很优雅.由于这种模式最终必须在代码的几个点重复,我想简化一点,并可能将它包装在一个可重用的对象中.我的第一次尝试涉及使用RAII来处理信号处理程序的设置/拆除(需要完成,因为每个函数需要不同的错误处理).所以我想出了这个:
template <int N>
struct signal_guard {
signal_guard(void (*f)(int)) {
sigemptyset(&new_sa.sa_mask);
new_sa.sa_handler = …
Run Code Online (Sandbox Code Playgroud) 我已经读过传入的jmp_buf变量中的setjmp"保存程序状态",但是我还没有找到任何关于它究竟是什么的描述.它是否复制了所有应用程序的内存?只是寄存器?堆栈?
#include <setjmp.h> #include <vector> int main(int argc, char**) { std::vector<int> foo(argc); jmp_buf env; if (setjmp(env)) return 1; }
用GCC 4.4.1编译上面的代码,g ++ test.cc -Wextra -O1,给出了这个令人困惑的警告:
/usr/include/c++/4.4/bits/stl_vector.h: In function ‘int main(int, char**)’: /usr/include/c++/4.4/bits/stl_vector.h:1035: warning: variable ‘__first’ might be clobbered by ‘longjmp’ or ‘vfork’
stl_vector.h的第1035行是在构造foo时调用的vector(n,value)构造函数使用的辅助函数中.如果编译器可以找出参数值(例如它是一个数字文字),警告就会消失,所以我在这个测试用例中使用了argc,因为编译器无法确定它的值.
我猜这个警告可能是因为编译器优化了向量构造,所以它实际上发生在setjmp着陆点之后(当构造函数参数依赖于函数的参数时,这似乎就是这种情况).
我怎样才能避免这个问题,最好不必将setjmp部分分解为另一个函数?
不使用setjmp不是一个选项,因为我遇到了一堆需要使用它来进行错误处理的C库.
在不久前的博客文章中,Scott Vokes描述了与lua使用C函数实现协程相关的技术问题,setjmp
并且longjmp
:
Lua协同程序的主要限制是,由于它们是用setjmp(3)和longjmp(3)实现的,所以你不能用它们从Lua调用C代码调用回调用回调用C的Lua,因为嵌套的longjmp将破坏C函数的堆栈帧.(这是在运行时检测到的,而不是静默失败.)
我没有发现这在实践中是一个问题,我不知道有什么方法可以修复它而不损坏Lua的可移植性,这是我最喜欢的Lua之一 - 它几乎可以运行任何ANSI C编译器和适度的空间.使用Lua意味着我可以轻装上阵.:)
我已经使用了很多协同程序,我认为我已经广泛地理解了发生了什么setjmp
,longjmp
做了什么和做了什么,但是我在某个时候读到了它,并意识到我并没有真正理解它.为了弄明白这一点,我尝试制作一个我认为应该根据描述引起问题的程序,相反它似乎工作正常.
然而,我看到其他一些地方人们似乎声称存在问题:
问题是:
这是我制作的代码.在我的测试中,它与lua 5.3.1链接,编译为C代码,测试本身在C++ 11标准下编译为C++代码.
extern "C" {
#include <lauxlib.h>
#include <lua.h>
}
#include <cassert>
#include <iostream>
#define CODE(C) \
case C: { \
std::cout << "When returning to " << where << " got code '" #C "'" << std::endl; \
break; \
}
void handle_resume_code(int code, const char * where) {
switch (code) {
CODE(LUA_OK) …
Run Code Online (Sandbox Code Playgroud) 嗨,我想在C中阅读关于setjmp/longjmp的好教程.如果有一些真实而非人为的例子会更好.
谢谢.
我正在浏览各种setjmp
和longjmp
实现的来源,并注意到并非所有的CPU寄存器都保存在jmp_buf
结构中.在查看AMD64 ABI之后,我注意到只保存了被调用者保存的寄存器.
我不明白当只保存了一些寄存器时,如何完全恢复功能状态.当然,未保存的寄存器必须反复多次破坏,直到我longjmp
稍后再打电话为止?
然而,一切都很完美,所以肯定有一些我不明白的东西.我希望有人可以对此有所了解.
谢谢!