C++崩溃恢复

Crash recovery in C++

本文关键字:恢复 崩溃 C++      更新时间:2023-10-16

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

有没有办法防止共享库中发生崩溃或段错误时应用程序崩溃和退出?

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

正如Itwastpete回答的那样,您可以设置(使用sigaction(2)SA_SIGINFO,不要使用signal(2)!)一个信号处理程序用于SIGSEGV。但是,请先仔细阅读信号(7)。

请注意,如果你想完全捕获SIGSEGV(或其他异步信号,如SIGBUSSIGILLSIGFPE等)并继续处理,这是棘手的,并且特定于机器。如果你从SIGSEGV中恢复正常,那么机器状态保持不变,执行回到触发SIGSEGV的机器指令,该指令无限重新出现(你陷入了一个无限循环)。

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

如果你想从你的信号处理程序中显示一个很好的回溯,请考虑使用例如来自最近的 GCC 源球内部的 libbacktrace。(如果程序和插件都使用调试信息进行编译,例如使用gcc -O -g)

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

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

一般来说,可靠的崩溃恢复确实很困难,特别是对于C++软件。您需要了解很多实现细节。使用独家自由软件有很大帮助:您可以研究所有库(甚至是libstdc++libc:您可能需要了解malloc实现的内部结构......

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

是的,这是可能的。如果发生段错误,您的程序将首先收到SIGSEGV(参见信号或由于信号已过时的 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......取决于您的应用。