让我们假设我有以下C代码:
static void handler(int sig, siginfo_t *si, void *unused)
{
printf("BOOM!n");
//another segfault here
exit(-1);
}
int main(int argc, char *argv[])
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = handler;
if (sigaction(SIGSEGV, &sa, NULL) == -1)
perror("failed to set handler");
// call other stuff here, somewhere in the callstack a segfault happens
}
如果执行遇到第一个分段错误,将触发处理程序。但是,当处理程序本身由于某种原因发生分段错误时,会发生什么呢?它会用新的siginfo_t
再次调用处理程序吗?还是会立即终止程序?
如果再次调用处理程序,类似这样的操作会起作用吗:
int in_handler = 0;
static void handler(int sig, siginfo_t *si, void *unused)
{
printf("BOOM!n");
if(!in_handler)
{
in_handler = 1;
//code that could possibly segfault
} else {
//totally safe code
}
exit(-1);
}
我之所以这么问,是因为我目前正在C中编写某种单元测试框架。如果一个测试用例由于某些非法内存访问而失败,我想将所有剩余的测试标记为失败。如果由于某种原因,测试把堆搞得非常糟糕,那么这个操作可能会失败,因为我必须迭代数据结构并将该信息写入xml文件。但是,如果将所有剩余的测试标记为失败,我仍然希望保留哪项测试是失败的信息。
来自sigaction(2)
:的手册页
#include <signal.h> int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); ....
sigaction
结构被定义为类似的东西struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); }
sa_mask
给出了在信号处理程序的执行。此外除非SA_NODEFER
标志为习惯于
因为在设置信号处理程序时没有设置SA_NODEFER
标志,所以如果在信号处理程序中发生另一个segfault,就不会再次调用它。一旦您退出,先前被阻止的信号将被传递。
至少Linux将使用SIGSEGV的默认信号处理程序,如果它发生在SIGSEGV的处理程序中,请参阅kernel/signal.c,这样您的处理程序就不会被再次调用 引用的代码似乎只在segfault发生时使用,而内核试图为信号处理程序设置堆栈帧(例如,因为堆栈指针无效)。
由于当您的segfault处理程序segfault时,您的程序将处于非常坏的状态,因此我不会依赖任何东西,而是使用一些处理这种情况的包装器。
除此之外,为了兼容POSIX,您不能在信号处理程序中使用printf或任何类似的东西。有关可以使用的函数,请检查man 7 signal
(查找安全函数)。write()
在这个列表中,所以您可以使用它编写自己的输出函数。