【问题标题】:Portable way to catch signals and report problem to the user捕获信号并向用户报告问题的便携式方式
【发布时间】:2010-09-11 07:45:36
【问题描述】:

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

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

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

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

是否有一种可移植的方式来处理信号并向用户提供信息?

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

【问题讨论】:

    标签: c++ c posix signals reentrancy


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

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

      通过使用该表中的“写入”命令,以下相对“丑陋”的解决方案有望解决问题:

      #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

      【讨论】:

      • 很好的答案,从中学到了一些新东西,太糟糕了,原来的发帖人没有回来投票/选择答案:)
      • 感谢您的评论。顺便说一句 - 这 OP的答案,即。我。我仍然不能 100% 确定这是正确的,因此我不会将其标记为已回答。
      【解决方案3】:

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

      #ifdef SIGUSR1 /* or whatever */
      

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

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

      【讨论】:

        【解决方案4】:

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

        致 bk1e(评论一些帖子) SIGSEGV 是同步的,这就是为什么您不能使用并非设计为可重入的函数的原因。如果崩溃的函数持有一个锁,而信号处理程序调用的函数试图获取相同的锁怎么办?

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

        • 异步信号处理程序是 (一般)希望回来 恢复正常程序执行。一种 同步信号的处理程序是 (通常)将要终止 无论如何,所以如果 你崩溃了。
        • 从反常的意义上说,您可以绝对控制何时传送同步信号 - 它发生在您执行有缺陷的代码时,并且不会在其他时间发生。您完全无法控制何时传递异步信号。除非 OP 自己的 I/O 代码本身就是缺陷的原因 - 例如输出 bad char* - 他的错误消息有合理的成功机会。

        【讨论】:

          猜你喜欢
          • 2013-09-03
          • 2014-06-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-12-12
          • 1970-01-01
          相关资源
          最近更新 更多