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

发布于 2024-07-05 21:09:47 字数 593 浏览 7 评论 0原文

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

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

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

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

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

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

If by some miracle a segfault occurs in our program, I want to catch the SIGSEGV and let the user (possibly a GUI client) know with a single return code that a serious problem has occurred. At the same time I would like to display information on the command line to show which signal was caught.

Today our signal handler looks as follows:

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

I can hear the screams of horror with the above, as I have read from this thread that it is evil to call a non-reentrant function from a signal handler.

Is there a portable way to handle the signal and provide information to users?

EDIT: Or at least portable within the POSIX framework?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

十二 2024-07-12 21:09:48

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

#ifdef SIGUSR1 /* or whatever */

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

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

FWIW, 2 is standard error on Windows also, but you're going to need some conditional compilation because their write() is called _write(). You'll also want

#ifdef SIGUSR1 /* or whatever */

etc around all references to signals not guaranteed to be defined by the C standard.

Also, as noted above, you don't want to handle SIGUSR1, SIGHUP, SIGINT, SIGQUIT and SIGTERM like this.

吃不饱 2024-07-12 21:09:48

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

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

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


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

Richard, still not enough karma to comment, so a new answer I'm afraid. These are asynchronous signals; you have no idea when they are delivered, so possibly you will be in library code which needs to complete to stay consistent. Signal handlers for these signals are therefore required to return. If you call exit(), the library will do some work post-main(), including calling functions registered with atexit() and cleaning up the standard streams. This processing may fail if, say, your signal arrived in a standard library I/O function. Therefore in C90 you are not allowed to call exit(). I see now C99 relaxes the requirement by providing a new function _Exit() in stdlib.h. _Exit() may safely be called from a handler for an asynchronous signal. _Exit() will not call atexit() functions and may omit cleaning up the standard streams at the implementation's discretion.

To bk1e (commenter a few posts up)
The fact that SIGSEGV is synchronous is why you can't use functions that are not designed to be reentrant. What if the function that crashed was holding a lock, and the function called by the signal handler tries to acquire the same lock?

This is a possibility, but it's not 'the fact that SIGSEGV is synchronous' which is the problem. Calling non-reentrant functions from the handler is much worse with asynchronous signals for two reasons:

  • asynchronous signal handlers are
    (generally) hoping to return and
    resume normal program execution. A
    handler for a synchronous signal is
    (generally) going to terminate
    anyway, so you've not lost much if
    you crash.
  • in a perverse sense, you have absolute control over when a synchronous signal is delivered - it happens as you execute your defective code, and at no other time. You have no control at all over when an async signal is delivered. Unless the OP's own I/O code is ifself the cause of the defect - e.g. outputting a bad char* - his error message has a reasonable chance of succeeding.
若相惜即相离 2024-07-12 21:09:48

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

Write a launcher program to run your program and report abnormal exit code to the user.

小女人ら 2024-07-12 21:09:47

列出了 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'也不应该从信号处理程序中调用!

正如嘶嘶声,上面调用_Exit对于HUP、TERM等信号来说是大锤式的做法。 理想情况下,当捕获到这些信号时,可以使用“易失性 sig_atomic_t”类型的标志来通知主程序它应该退出。

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

  1. Unix 信号编程简介
  2. 扩展传统信号

This table lists all of the functions that POSIX guarantees to be async-signal-safe and so can be called from a signal handler.

By using the 'write' command from this table, the following relatively "ugly" solution hopefully will do the trick:

#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
}

EDIT: Building on windows.

After trying to build this one windows, it appears that 'STDERR_FILENO' is not defined. From the documentation however its value appears to be '2'.

#include <io.h>
#define STDIO_FILENO 2

EDIT: 'exit' should not be called from the signal handler either!

As pointed out by fizzer, calling _Exit in the above is a sledge hammer approach for signals such as HUP and TERM. Ideally, when these signals are caught a flag with "volatile sig_atomic_t" type can be used to notify the main program that it should exit.

The following I found useful in my searches.

  1. Introduction To Unix Signals Programming
  2. Extending Traditional Signals
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文