我们在代码库中实现了“longjmp-Restore堆栈环境”。 longjmp
例程由特定的error_exit
函数调用,可以从任何地方调用。
因此有可能在调用longjmp
时,可能没有调用setjmp
例程,并且缓冲区可能具有导致崩溃的无效值。
我可以将缓冲区初始化为NULL
,还是可以检查是否有未设置或无效的值。一种方法是每当调用setjmp
时我都可以设置一个标志变量,我可以检查它。但这只是一个黑客攻击。
void error_exit()
{
extern jmp_buf buf;
longjmp(buf, 1);
return 1;
}
我可以这样做吗?
void error_exit()
{
extern jmp_buf buf;
if(buf)
longjmp(buf, 1);
return 1;
}
代码是混合的C / C ++,我知道我可以在任何地方用C ++异常处理替换setjmp
和longjmp
,但现在这是不可能的,我可以用longjmp
来代替{{1}}导致崩溃的无效缓冲区?
答案 0 :(得分:2)
jmp_buf
没有特别好的记录。在linux标题中,您可以找到类似的内容:
typedef int __jmp_buf[6];
struct __jmp_buf_tag {
__jmp_buf __jmpbuf; /* Calling environment. */
int __mask_was_saved; /* Saved the signal mask? */
__sigset_t __saved_mask; /* Saved signal mask. */
};
typedef struct __jmp_buf_tag jmp_buf[1];
将其设置为零然后测试整个尺寸可能会浪费时间。
就个人而言,我会保留一个指向此缓冲区的指针,将其初始化为NULL并在setjmp之前设置它。
jmp_buf physical_buf;
jmp_buf *buf = NULL;
...
buf = &physical_buf;
if (setjmp(*buf)) {
...
}
与拥有单独的旗帜的想法相同。此外,您可以根据需要动态分配jmp缓冲区。
答案 1 :(得分:2)
知道缓冲区是否已设置不是一个真正的问题,你可以只有一个包含该信息的辅助变量。
真正的问题是,你不能longjmp
横向,这是未定义的行为,并且由于根本原因无法工作。从您调用setjmp
的函数返回后,该函数的堆栈将被无效并被后续调用覆盖。因此setjmp
上下文不再有效。我不是在谈论jmp_buf
本身,而是谈论记录在其中的堆栈状态。
所以你唯一能做的就是有一个辅助变量来记录你是否设置了缓冲区,并且一旦你将问题留给了setjmp
,你就会立即取消设置。
答案 2 :(得分:1)
好问题。
据我所知,从POSIX标准(http://pubs.opengroup.org/onlinepubs/9699919799/functions/longjmp.html http://pubs.opengroup.org/onlinepubs/9699919799/functions/setjmp.html http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/setjmp.h.html)jmp_buf
是不透明的,除了后者引用要求它是一个数组类型:
<setjmp.h>
标头应定义数组类型jmp_buf
和...sigjmp_buf
。
我相信在实践中你会保存bzero()
- 缓冲区,并在跳转之前将它与零进行比较,但理论上,对于某些人而言,所有零jmp_buf
都是允许的。被发现POSIX系统。如果你对此感到困扰(我不会),可以选择set_jmp
虚拟缓冲区到你从未使用的某个跳转点,然后memcmp
你的jmp_buf
对此在做跳跃之前做假jmp_buf
。这样你就只能使用合法的(即由set_jmp
)jmp_buf
s。
另一种方法是维护一个单独的标志,指示它是否已被初始化(或在struct
中包装)。
我认为我应该在联机帮助页set_jmp
和long_jmp
上插入强制警告,因为它会使应用程序难以阅读,并且对信号处理产生不可预测的影响(后者使用{{ 1}})。此外,在C ++上下文中,它显然不会展开您的异常处理程序。