在信号处理程序中捕获 C++ 未处理的异常并恢复应用程序

Catch a C++ unhandled exception in the signal handler and resume application

首先是一些上下文:

我正在测试一些 C 语言编写的服务的稳健性,运行 在嵌入式 Linux 中。我所有的测试都是用 C++ 编写的,并调用服务的 C API。

请注意,服务和应用程序 运行 在 2 个单独的进程中。应用程序在其上下文中打开代理以通过 tcp/ip 与服务通信。

为了检查错误编码的回调不会中断服务,我给它一个函数,它只是抛出一个 C++ 异常。正如预期的那样,在此 C 回调中抛出异常会导致应用程序崩溃。

到目前为止,该服务似乎对此很稳健:回调是从客户端应用程序上下文中 运行 的线程调用的。这意味着,只有客户端应用程序崩溃了,而服务仍然存在。

当我说应用程序崩溃时,我的意思是它接收到 SIGABRT 信号,这是来自 gdb 的调用堆栈:

(gdb) info stack
0  0x4c22cb94 in raise () from /lib/libc.so.6
1  0x4c230670 in abort () from /lib/libc.so.6
2  0xb6e9e6c4 in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib/libstdc++.so.6
3  0xb6e9c214 in ?? () from /usr/lib/libstdc++.so.6
4  0xb6e9c288 in std::terminate() () from /usr/lib/libstdc++.so.6
5  0xb6e9c5ac in __cxa_throw () from /usr/lib/libstdc++.so.6
6  0x0011824c in LocationTest::crashingLocCb (location=<optimized out>)
at ../../../TestLibrary/200-Location/src/locationtest.cpp:427
7  0x00144f64 in locationCb (location=<optimized out>)
at ../../PAL/src/tms/pallocationprovider.cpp:109
8  0xb6fbdb50 in locationcallback_thread (thread_info=0x1a82b8)
at ../lib_c/src-gen/location_proxy.c:273
9  0x4c33defc in ?? () from /lib/libpthread.so.0

我现在想做的事情:

我想捕获信号 SIGABRT 并恢复测试应用程序,以便更进一步(检查服务是否有一些内部状态损坏;检查它没有导致一些泄漏等。)

让东西尽可能干净的最佳方法是什么?

这将通过信号处理程序来完成,但是其他 运行 线程会受到伤害吗?那么将信号处理程序中捕获的信息发送回相关线程的最佳方法是什么?

(我对那些 posix 信号还没有太多经验)

这是我解决问题的方法(伪代码)。可能还有其他方法,我仍然洗耳恭听:

void malfunction()
{

    terminate_handler previousTerminateHandler = set_terminate(callbackUnhandledExceptionTerminateHandler);

    registerCallback(throwingCb);
    launchTheTest();
    waitForErrorEvent();
    saveTheResult();
    doSomeCleanUp();

    set_terminate(previousTerminateHandler);

    return;
}

static void callbackUnhandledExceptionTerminateHandler()
{
    try{ throw; }
    catch(const exception& e) { 
        cout << e.what() << endl;
        notifyTheErrorEvent();
    }catch(...){
        cout << "callbackUnhandledExceptionTerminateHandler : else ???" << endl;
        abort();
    }

    //Log whatever information, you need such as pid, stack or whatever
    cout << "processId = " << getpid() << endl;

    //Here if you return from this handler, it will call abort();
    //You could let it ends, if you already logged what you needed.
    //You could also trap the thread, if this is something you already do elsewhere before stopping the application.

}

在我的例子中,我可以检查该服务是否能够适应客户应用程序的崩溃,并且仍然能够让其他人(甚至是同一个人!)打开另一个代理并做更多的工作。现在剩下要做的就是确保这里没有内存泄漏。 (只回答谁会质疑为什么这一切)。

在更一般的情况下,同样的处理可用于在未处理异常时打印堆栈,而不需要 gdb。