C++ 中的崩溃恢复

Posted

技术标签:

【中文标题】C++ 中的崩溃恢复【英文标题】:Crash recovery in C++ 【发布时间】:2014-01-18 03:05:46 【问题描述】:

我有一个在 Linux 环境中用 C++ 编写的应用程序。应用程序在运行时动态加载库(共享对象)。 (应用程序获取用户命令,它会执行逻辑来动态加载所需的共享库。)

在共享库发生崩溃或段错误时,有什么方法可以防止应用程序崩溃退出?

我希望我的应用程序处于活动状态并向用户报告崩溃。

【问题讨论】:

cpp 是 C 预处理器。 C++ 是语言。 它是一个什么样的应用程序?它有多重要?告诉我们更多信息! 【参考方案1】:

作为Itwasntpete answered,您可以设置(使用sigaction(2) SA_SIGINFO,不要使用signal(2)!)SIGSEGV 的信号处理程序。不过,请先仔细阅读signal(7)。

请注意,如果您想完全捕获SIGSEGV(或其他异步信号,如SIGBUSSIGILLSIGFPE 等...)并继续处理,这很棘手,而且是特定于机器的。如果您从您的SIGSEGV 明确返回,那么机器状态保持不变,并且执行返回到触发SIGSEGV 的机器指令,该指令将重新触发ad infinitum(您是陷入无限循环)。

因此,为了能够继续执行,您不应该从信号处理程序返回,或者在其中使用siglongjmp(3) 跳转到先前使用sigsetjmp(3) 注册的状态,或者更改机器状态。要更改机器状态,您可以使用mmap(2) 和相关调用更改address space,或者您可以使用作为第三个参数传递给您的处理程序的ucontext_t* 更改一些[保存的] 处理器寄存器,并查询详细信息使用作为第二个参数传递的siginfo_t* 的信号信息。如何做到这一点是系统特定的(取决于操作系统和处理器)并且很棘手。

如果您想从信号处理程序中显示一个不错的回溯,请考虑使用例如libbacktrace 来自最近的 GCC 源球。 (如果程序和插件都使用调试信息编译,例如使用gcc -O -g,它会更好地工作)

请注意,signal(7) 明确表示只有 异步信号安全函数 可以(直接或间接)从信号处理程序中调用。所以原则上,禁止从信号处理程序调用malloc::operator new(大多数C++ containers!!)或printf 是被禁止的,而且是不明智的。但是,如果您只调用 libbacktrace 函数,然后从信号处理程序中调用 _exit(2),这通常(但原则上并非总是)有效。

如果您希望您的应用程序报告错误并保持活动状态(例如,如果您的应用程序是服务器,则能够继续处理大量请求),这可能会非常棘手(有时不可能的)。例如,如果插件错误到有corrupted the heap,你应该清理混乱(这并不总是可能的)......在某些情况下,我想唯一要做的就是重新启动应用程序(例如,通过从信号处理程序内部调用execve(2))。 Application checkpointing 技术可能是相关的:您可以将应用程序设计为定期检查点并从最新保存的状态重新启动...

一般可靠的崩溃恢复确实很困难,特别是对于 C++ 软件。你需要了解很多实现细节。完全使用free software 有很大帮助:您可以研究所有库中的细节(甚至libstdc++libc:您可能需要了解malloc 的实现内部...)。

我什至不确定这是插件的正确方法。您也许可以考虑帮助插件开发人员,例如通过明确一些定义明确的应用程序特定编码规则(或programming style)并可能开发一些 GCC 编译器扩展,例如使用MELT 在插件编译时检查其中一些。

【讨论】:

【参考方案2】:

是的,这是可能的。 If a segfault occurs,您的程序将首先收到SIGSEGV(请参阅signal 或因为信号已过时sigaction(2))。将此信号连接到处理程序可让您生成崩溃报告。

void crash(int sig) 
  cout << "report crash";
  exit(sig);


int main() 
  // connect signal to handler
  signal(SIGSEGV, crash);

  return 0;

正如 Jonathan Leffler 提到的他的评论,这只是一个小建议。有一些信号不仅应该被捕捉到 SIGSEGV,而且可能还应该被捕捉到 SIGILLSIGFPE... 取决于您的应用程序。

【讨论】:

效果如何取决于导致分段错误的损坏的性质。此外,从技术上讲,您的信号处理程序不应调用标准 I/O 函数。由于exit() 运行atexit() 处理程序之类的东西,它也可能不是完全安全的。尽管如此,你的建议是做什么的核心。 请注意,signal(2) 已过时。它的手册页说“避免使用它:改用sigaction(2)”

以上是关于C++ 中的崩溃恢复的主要内容,如果未能解决你的问题,请参考以下文章

北亚数据恢复昆腾系列存储服务器StorNext文件系统RAID中的2块硬盘先后故障离线,RAID崩溃的数据恢复案例

Phonegap 恢复应用程序崩溃

服务器数据恢复raid损坏导致戴尔某型号服务器崩溃的数据恢复案例

服务器数据恢复raid5崩溃导致lvm信息和VXFS文件系统损坏的数据恢复案例

存储数据恢复EMC存储raid5崩溃导致上层lun无法使用的数据恢复案例

## 分布式事务面试官问我:MySQL中的XA事务崩溃了如何恢复??