如何将 SIGFPE 转换为C++例外

How to convert SIGFPE into a C++-exception

本文关键字:C++ 例外 转换 SIGFPE      更新时间:2023-10-16

在Win32下,很容易将SEH异常转换为_set_se_translator C++异常。在 Linux 上是否有类似的方法将某些信号转换为C++异常?我需要将 SIGFPE 映射到C++异常。

使用 g++,您可以使用-fnon-call-exceptions选项,只需从 FPE 信号处理程序中抛出异常。请注意,并非每个信号都可以这样映射,只有捕获指令产生的信号才能映射。幸运的是,SIGFPE就是这样一个信号。

在 Linux 等 POSIX 系统上,您无法可靠地做到这一点。有关更多详细信息,另请参阅此处。请注意,在你的情况下不能使用 signalfd(2)。我假设您正在x86-64或其他一些常用的体系结构上使用Linux。

仔细阅读 signal(7)(特别是关于信号处理程序中的异步信号安全函数的说法,它们是处理像 SIGFLE 这样的信号的唯一方法)。另请阅读 C++11 标准或 C99 标准对signal的看法。大多数C++实现有时可能会生成一些对运行时支持函数的隐式调用,这些函数不是异步信号安全的(特别是那些用于引发异常的函数)。因此,您无法可靠地从信号处理程序引发异常)。

实际上,以下内容将是错误的信号处理程序

/// WRONG CODE, against signal(7) since calling 
/// non-async-signal-safe functions from the C++ runtime
void badSIGFPEhandler(int sig) {
  if (sig == SIGFPE)
    throw std::runtime_error("got SIGFPE");
}    

你可以通过使用g++ -Wall -O -fverbose-asm -S编译它(然后查看发出的.s汇编程序文件)来检查它是否正在调用一些非异步信号安全函数(来自C++运行时),如 __cxa_allocate_exception__cxa_throw_Unwind_Resume 信号(7) 禁止的函数。

在实践中,对于

abort_exit的信号处理程序,唯一安全的做法是设置一些volatile sigatomic_t标志,或使用一些异步信号安全函数,例如在管道(7)上写入(2)某些内容。此外,从信号处理程序引发异常并不比从中调用printf更糟糕(很多人都错误地这样做);这是被禁止的,但它通常可以工作。我仍然不建议这样做,特别是在长时间运行的程序中,或者崩溃 - 即使不经常 - 也是不可接受的。

阅读更多关于未定义行为

的信息,特别是Lattner的博客,关于每个C程序员都应该知道的关于未定义行为的知识。

在实践中,处理信号的唯一可靠和可移植的方法是拥有一个信号处理程序,它只是设置一些volatile sigatomic_t标志。但是,如果您这样做SIGFPE您的实现很可能会在相同的状态下重新启动相同的计算,从而无限期地循环SIGFPE处理。另请参阅有关-fnon-call-exceptions的这个(所以我相信n.m.的答案可能并不总是正确和可靠的;事实上,它是未定义的行为,通常看起来有效)。

实际上,我强烈怀疑Windows进行转换的方式不符合C++11或C++14标准(或C99或C11标准),您可以在Windows上有一个符合C++(或C99或C11)的标准实现,这不允许做你的建议;可能Clang或GCC就是这样的实现。