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

时间:2020-03-06 14:26:42  来源:igfitidea点击:

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

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

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

我从上面的线程中读到,从信号处理程序中调用非可重入函数是邪恶的,因此,我可以听到上面的恐怖尖叫声。

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

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

解决方案

编写启动程序以运行程序,并将异常退出代码报告给用户。

该表列出了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

编辑:"退出"也不应从信号处理程序中调用!

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

我发现以下内容对我的搜索有用。

  • Unix信号编程简介
  • 扩展传统信号

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

#ifdef SIGUSR1 /* or whatever */

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

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

理查德,尚无足够的因果可言,所以恐怕会有一个新答案。这些是异步信号;我们不知道它们何时交付,因此可能我们将处于需要完成以保持一致的库代码中。因此,需要返回这些信号的信号处理程序。如果调用exit(),则库将在post-main()之后执行一些工作,包括调用在atexit()中注册的函数并清理标准流。例如,如果信号到达标准库I / O函数,则此处理可能会失败。因此,在C90中,我们不允许调用exit()。我现在看到C99通过在stdlib.h中提供一个新函数_Exit()放宽了要求。可以从处理程序中安全地针对异步信号调用_Exit()。 _Exit()不会调用atexit()函数,并且可以根据实现情况自行决定是否清理标准流。

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

这是可能的,但这不是" SIGSEGV是同步的事实"。对于异步信号,从处理程序中调用非可重入函数的情况要差得多,这有两个原因:

  • 异步信号处理程序(通常)希望返回并恢复正常的程序执行。同步信号的处理程序(通常)将终止,因此如果崩溃,我们不会损失太多。
  • 从反常的角度来说,我们可以完全控制何时传递同步信号-它在我们执行有缺陷的代码时发生,而在其他时间也不会发生。传递异步信号时,我们根本无法控制。除非OP自己的I / O代码本身就是造成缺陷的原因-例如输出一个错误的字符*-他的错误消息很有可能成功。