c-SIGSEGV处理程序中的Segfault



让我们假设我有以下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()在这个列表中,所以您可以使用它编写自己的输出函数。

最新更新