C++代码在内存越界或线程竞争情况下很容易出现未定义行为段错误,导致程序崩溃挂掉生成coredump,但如果想让程序遇到这种情况仍然可以继续运行,不中断服务呢?这里展示了一种方法。
C++代码发生段错误后系统会抛出SIGSEGV 信号 ,之后 调用默认的信号处理函数 ,产生core文件 ,然后关闭程序。让程序不挂掉的办法就是捕获这个中断信号,调用自定义的信号处理函数,并且叠加一些代码跳转的方法。
单纯捕捉信号不做特殊处理,虽然进程仍然在,但是会一直报错阻塞主线程,程序完全不可用。
有效的做法是在发生段错误的代码之前保存程序执行上下文,信号捕捉后通过跳转函数直接跳过段错误的代码,从而达到程序继续执行的目的。
这里用到两个函数sigsetjmp和siglongjmp,前者会保存当前的信号屏蔽表 (signal mask),后者跳转的时候会恢复线程的屏蔽表。通过设置的参数传递上下文信息。
代码:
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <stdarg.h>
#include <thread>
#include <chrono>
sigjmp_buf env;
void handle_signal(int sig)
{
printf("# handle_signal %d\n", sig);
// jump to continue code point
siglongjmp(env, 1);
}
int main(int argc,char** argv)
{
// global register signal handle
signal(SIGSEGV, handle_signal);
for (int i = 0; i < 10; i++)
{
// save context
int r = sigsetjmp(env, 1);
if(r == 0)
{
// execute different code dynamically
if (i % 2)
{
printf("execute the bad code\n");
int* p = nullptr;
*p = 7;
}
else
{
printf("execute the good code\n");
int p = 7;
}
}
else
{
// jump from signal handle
printf("skip the bad code, i = %d\n", i);
}
}
while(true)
{
printf("--- I am alive ---\n");
std::this_thread::sleep_for(std::chrono::seconds(1));
}
return 0;
}
执行结果:
execute the good code
execute the bad code
# handle_signal 11
skip the bad code, i = 1
execute the good code
execute the bad code
# handle_signal 11
skip the bad code, i = 3
execute the good code
execute the bad code
# handle_signal 11
skip the bad code, i = 5
execute the good code
execute the bad code
# handle_signal 11
skip the bad code, i = 7
execute the good code
execute the bad code
# handle_signal 11
skip the bad code, i = 9
--- I am alive ---
--- I am alive ---
--- I am alive ---
--- I am alive ---