4

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

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

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

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

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

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

4

4 回答 4

12

列出了 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
}

编辑:建立在窗户上。

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

#include <io.h>
#define STDIO_FILENO 2

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

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

我发现以下内容在我的搜索中很有用。

  1. Unix 信号编程简介
  2. 扩展传统信号
于 2008-09-19T17:32:48.007 回答
1

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

#ifdef SIGUSR1 /* or whatever */

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

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

于 2008-09-19T19:35:24.920 回答
1

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

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

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

  • 异步信号处理程序(通常)希望返回并恢复正常的程序执行。同步信号的处理程序(通常)无论如何都会终止,因此如果您崩溃,您不会损失太多。
  • 在反常的意义上,您可以绝对控制何时传递同步信号 - 它发生在您执行有缺陷的代码时,并且不会在其他时间发生。您完全无法控制何时传递异步信号。除非 OP 自己的 I/O 代码本身就是缺陷的原因——例如输出错误的 char*——否则他的错误消息有合理的成功机会。
于 2008-09-22T11:54:05.360 回答
0

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

于 2008-09-19T16:39:56.027 回答