【问题标题】:How to convert SIGFPE into a C++-exception如何将 SIGFPE 转换为 C++ 异常
【发布时间】:2016-07-06 10:12:26
【问题描述】:

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

【问题讨论】:

  • 也许你应该编辑你的问题来改进和激励它,特别是解释你为什么要这样做,以及你真正考虑SIGFPE的确切情况.
  • @Basile Starynkevitch 对于每个经验最少的软件工程师来说,这样做的动机应该是显而易见的!
  • 一种更好(通常更快,当然更便携)的做法是避免整数除以零或(使用 IEEE 双精度数)使用信号 NAN。请参阅this answer 以获得相关答案,并在signal(7) 之后阅读signal-safety(7),当然还有floating-point-gui.de

标签: c++ linux exception signals


【解决方案1】:

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

【讨论】:

  • 你确定它总是能可靠地工作(例如,在一些析构函数中除以零,也许是一个例外)
  • @BasileStarynkevitch 这就像从析构函数中抛出一样。你为什么要在析构函数中做浮点运算?
  • 我的感觉是这种做法会产生通常可能有效的未定义行为。
  • GCC documentation 提到了-fnon-call-exceptions“生成允许捕获指令抛出异常的代码。请注意,这需要特定于平台的运行时支持,但并非无处不在。此外,它只允许捕获指令抛出异常,即内存引用或浮点指令。它不允许从任意信号处理程序(如 SIGALRM)抛出异常。”。我和你一样不明白。
  • @BasileStarynkevitch 在之前的评论中,我声明了文档暗示所以没有这样,并且它应该这么说,它目前没有。没有办法对开关的动作给出任何其他有用的解释。如果开关没有任何用处,则应将其完全删除。
【解决方案2】:

在 POSIX 系统(例如 Linux)上,您无法可靠地做到这一点。另请参阅this 了解更多详情。请注意,在您的情况下,您不能使用 signalfd(2)。我假设您在 x86-64 或其他一些常用架构上使用 Linux。

仔细阅读signal(7)(特别是关于信号处理程序中的async-signal-safe函数的内容,它们是处理SIGFLE等信号的唯一方法)。另请阅读 C++11 标准或 C99 标准对signal 的评价。大多数 C++ 实现有时可能会生成一些 隐式调用 到运行时支持函数,这些函数不是 async-signal-safe(特别是那些用于引发异常的函数。所以你不能可靠地抛出来自信号处理程序的异常)。

在实践中,以下将是错误的信号处理程序

/// 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、@ 987654339@, _Unwind_Resumesignal(7)..禁止@....

在实践中,对于不abort_exit 的信号处理程序,唯一安全的做法是设置一些volatile sigatomic_t 标志,或使用少数async-signal-safe 函数,例如write(2) pipe(7) 上的东西。此外,从信号处理程序中抛出异常并不比从中调用printf 更糟糕(很多人都做错了);这是被禁止的,但它通常可以工作。我仍然不建议这样做,特别是在运行了很长时间的程序中,或者对于那些崩溃(即使是很少发生)是不可接受的。

阅读有关 undefined behavior 的更多信息,尤其是 Lattner 关于 What every C programmer should know about undefined behavior 的博客。

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

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

【讨论】:

  • 禁止从 sigbal 处理程序调用某些函数适用于用 C 编写的用户代码。这并不意味着实现本身不能调用此类函数,或者它不能保证未找到的其他函数的安全性在 POSIX 列表中。
  • 理论上你可能是对的,但实际上你错了,SIGFPE 可以通过整数零除法(在 x86-64 上)调用,它可以在任何地方发生(包括在实现标准容器的代码中,对于他们未定义的行为),当然在机器代码的任意点的用户代码中
  • 你不能责怪 -fnon-call-exceptions 因为与标准容器相关的不适当代码导致无法从 UB 中恢复。它不会向你保证。至于用户代码,-fnon-call-exceptions 的目的是让它在出现由陷阱引起的异常时表现良好,包括 FPE 陷阱。我不在乎编译器是如何做到的。如果您认为文档没有承诺,请针对文档提交错误。它使many users 相信存在这样的承诺。
  • 根据您的解释,您仍然没有说出文档所承诺的内容。如果它承诺了什么,说什么;如果它什么都不承诺,也许提交一个关于无用开关的错误。
猜你喜欢
  • 1970-01-01
  • 2010-09-10
  • 2011-10-08
  • 2023-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-02
相关资源
最近更新 更多