【问题标题】:Exception not caught after signal发出信号后未捕获异常
【发布时间】:2018-09-27 14:25:55
【问题描述】:

我尝试在我的代码中捕获一个终止信号,以便在退出之前编写一个重启文件。我的解决方案基于此answer

#include <exception>
#include <csignal>
#include <iostream>


class InterruptException : public std::exception
  {
   public:
    InterruptException(int _s) : signal_(_s) { }
    int signal() const noexcept
    {
      return this->signal_;
    }

   private:
    int signal_;
  };

  /// method to throw exception at signal interrupt
  void sig_to_exception(int s)
  {
    throw InterruptException(s);
  }

int main()
{
  // activate signal handling
  struct sigaction sigIntHandler;
  sigIntHandler.sa_handler = sig_to_exception;
  sigemptyset(&sigIntHandler.sa_mask);
  sigIntHandler.sa_flags = 0;
  sigaction(SIGINT, &sigIntHandler, NULL);

  try
  {
    for (std::size_t i = 0; i < 100000000; ++i)
    {
      std::cout  << i << std::endl;
    }
  }
  catch (const InterruptException& e)
  {
    std::cout << "Received signal " << e.signal() << std::endl;
    std::exit(1);
  }
  catch(...)
  {
    std::cout << "Other catch!" << std::endl;
  }
}

异常被抛出,但是,我的 catch 块没有捕获它。程序以未捕获的异常InterruptException 终止。我在 MacOS 上尝试了 clang 和 gcc。知道为什么没有正确捕获异常吗?

谢谢

使用 g++ 7.3.0 编译时的输出:

terminate called after throwing an instance of 'InterruptException'
   what():  std::exception
Abort trap: 6

使用 Apple LLVM 9.0.0 编译时的输出

libc++abi.dylib: terminating with uncaught exception of type InterruptException: std::exception

PS:当我使用 Apple LLVM 编译时,似乎有时会捕获异常,但并非总是如此,这使得这更加奇怪。

【问题讨论】:

  • 一个问题(我不知道答案)是在与应用程序的主线程相同的线程上引发的信号吗?如果不是,则用于抛出的 C++ 异常的堆栈将永远不会展开到捕获。
  • 我在我的代码中使用 OpenMP,但上面的示例是串行的并且显示了相同的行为。
  • 阅读:stackoverflow.com/questions/22005719/… ...进程中哪个线程将处理此信号尚未确定..."

标签: c++ exception exception-handling signals


【解决方案1】:

您可以在信号处理程序中可靠地做的事情很少。特别是,您不能抛出异常。问题中的代码(以及它链接到的“答案”)充其量依赖于编译器/操作系统特定的行为。有关您可以在信号处理程序中执行的操作的限制,请参阅this

请注意,上面的链接指的是signal,它是标准C。sigaction 不是标准C,它是POSIX,C++ 语言定义对使用它的程序没有任何要求。

【讨论】:

    【解决方案2】:

    在大多数系统上,信号处理程序使用的堆栈帧不是编译器为函数调用定义的标准函数堆栈帧。

    因此不支持抛出 sig 处理程序。

    Stack frame for signal handling in the Linux Kernel

    根据链接问题中的讨论,在 linux 系统上,他们甚至没有为堆栈帧使用相同的堆栈,并且返回需要跳回系统函数以恢复原始用户堆栈。

    除非操作系统是专门为处理异常而设计的,否则这是行不通的。

    信号处理程序的经验法则是在信号处理程序中做的越少越好。设置一个可以被您的正常代码检测到的全局标志,然后在您的正常代码中定期检查该标志以查看信号何时发生。

    【讨论】:

      猜你喜欢
      • 2016-01-13
      • 2012-09-15
      • 2014-08-13
      • 2021-09-02
      • 2010-09-28
      • 2012-05-31
      相关资源
      最近更新 更多