便携式捕获信号并向用户报告问题的方法

时间:2008-09-19 16:05:34

标签: c++ c posix signals reentrancy

如果由于某些奇迹在我们的程序中发生了段错误,我想抓住SIGSEGV并让用户(可能是GUI客户端)知道一个返回码,发生了严重的问题。同时,我想在命令行上显示信息,以显示捕获的信号。

今天我们的信号处理程序如下所示:

void catchSignal (int reason) {
  std :: cerr << "Caught a signal: " << reason << std::endl;
  exit (1);
}

我可以听到上述恐怖的尖叫声,正如我从thread中读到的那样,从信号处理程序中调用不可重入的函数是邪恶的。

是否有便携式方式处理信号并向用户提供信息?

编辑:或至少在POSIX框架内可移植?

4 个答案:

答案 0 :(得分:12)

这个table列出了POSIX保证异步信号安全的所有功能,因此可以从信号处理程序中调用。

通过使用此表中的'write'命令,以下相对“丑陋”的解决方案有望成功:

#include <csignal>

#ifdef _WINDOWS_
#define _exit _Exit
#else
#include <unistd.h>
#endif

#define PRINT_SIGNAL(X) case X: \
          write (STDERR_FILENO, #X ")\n" , sizeof(#X ")\n")-1); \
          break;

void catchSignal (int reason) {
  char s[] = "Caught signal: (";
  write (STDERR_FILENO, s, sizeof(s) - 1);
  switch (reason)
  {
    // These are the handlers that we catch
    PRINT_SIGNAL(SIGUSR1);
    PRINT_SIGNAL(SIGHUP);
    PRINT_SIGNAL(SIGINT);
    PRINT_SIGNAL(SIGQUIT);
    PRINT_SIGNAL(SIGABRT);
    PRINT_SIGNAL(SIGILL);
    PRINT_SIGNAL(SIGFPE);
    PRINT_SIGNAL(SIGBUS);
    PRINT_SIGNAL(SIGSEGV);
    PRINT_SIGNAL(SIGTERM);
  }

  _Exit (1);  // 'exit' is not async-signal-safe
}

编辑:在Windows上构建。

尝试构建此窗口后,似乎未定义“STDERR_FILENO”。然而,从文档中它的值似乎是'2'。

#include <io.h>
#define STDIO_FILENO 2

编辑:不应该从信号处理程序调用'exit'!

正如fizzer所指出的,在上面调用_Exit是一种用于HUP和TERM等信号的大锤方法。理想情况下,当捕获这些信号时,可以使用带有“volatile sig_atomic_t”类型的标志来通知主程序它应该退出。

以下我发现我的搜索很有用。

  1. Introduction To Unix Signals Programming
  2. Extending Traditional Signals

答案 1 :(得分:1)

FWIW,2也是Windows上的标准错误,但是你需要一些条件编译,因为它们的write()被称为_write()。你也想要

#ifdef SIGUSR1 /* or whatever */

等所有对信号的引用都不能保证由C标准定义。

另外,如上所述,您不希望像这样处理SIGUSR1,SIGHUP,SIGINT,SIGQUIT和SIGTERM。

答案 2 :(得分:1)

理查德,仍然没有足够的业力评论,所以一个新的答案,我害怕。这些是异步信号;你不知道什么时候交付,所以你可能会在库代码中需要完成以保持一致。因此,需要返回这些信号的信号处理程序。如果你调用exit(),那么库将在main()之后做一些工作,包括调用用atexit()注册的函数并清理标准流。例如,如果您的信号到达标准库I / O功能,则此处理可能会失败。因此,在C90中,您不能调用exit()。我现在看到C99通过在stdlib.h中提供一个新函数_Exit()来放宽要求。可以从异步信号的处理程序安全地调用_Exit()。 _Exit()不会调用atexit()函数,可能会根据实现自行决定省略清理标准流。

to bk1e(发表几篇文章) SIGSEGV是同步的这一事实是您不能使用不是可重入的函数的原因。如果崩溃的函数持有一个锁,并且信号处理程序调用的函数试图获取相同的锁,该怎么办?

这是一种可能性,但这不是“SIGSEGV同步的事实”,这就是问题所在。异步信号从处理程序调用非重入函数要差得多,原因有两个:

  • 异步信号处理程序 (一般)希望回归和 恢复正常程序执行。一个 同步信号的处理程序是 (通常)要终止 无论如何,如果你没有失去太多 你崩溃了。
  • 从某种意义上讲,您可以完全控制何时传送同步信号 - 它会在您执行有缺陷的代码时发生,并且在任何时候都没有。当交付异步信号时,您无法控制。除非OP自己的I / O代码本身就是缺陷的原因 - 例如输出错误的字符* - 他的错误消息有合理的成功机会。

答案 3 :(得分:0)

编写一个启动程序来运行程序并向用户报告异常退出代码。