最近有点懒了,好久没有更新博客了,今天来一发。
在代码开发过程中,我们经常会遇到程序core掉,这个时候正常的处理步骤是,我们保留程序的coredump,然后分析,进而找出程序的bug,fix it!
但是,如果你的程序是线上服务,而且只会有极少数的输入会导致程序core掉,而且你允许可以有少量错误发生,那么这时候,我们的一个直观想法是,有没有什么方法可以 跳过这些该死的极少数异常数据,使得程序继续执行?
特别是在用c++开发基于streaming的hadoop程序时,如果我们的代码里有一些很难找到的bug,这些bug对于特定的输入会导致程序core掉,从而导致整个hadoop job失败(如果你没有使用-D mapred.max.map.failures.percent 来设置hadoop job的失败率),是一个让人极度郁闷的事。(虽然是我们自己的bug...)
那么,到底有没有什么办法使得我们在遇到core的时候,返回到程序的某一个正确的位置,从而继续执行我们的程序呢?
linux c下面提供了一套API,用于实现函数之间的跳转,函数原型如下:
头文件:#include <setjmp.h>
函数原型:
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);
int sigsetjmp(sigjmp_buf env, int savesigs);
void siglongjmp(sigjmp_buf env, int val);
setjmp函数用于设置跳转的目的位置,longjmp函数进行跳转。
env:保留了需要返回的位置的堆栈情况。
savesigs:是否保存信号
setjmp的返回值:直接调用该函数,则返回0;若由longjmp的调用,导致setjmp被调用,则返回val(longjmp的第二个参数)。
在Linux中使用longjmp有一个问题,当捕捉到一个信号的时候进入信号处理函数,此时当前信号自动加入到进程的信号屏蔽字中,这就阻止了后来的这种信号中断该信号处理程序。如果用longjmp跳出信号处理程序,那么该进程的信号屏蔽字不会被恢复到调用信号处理程序前的信号屏蔽字。如果想恢复的话,就要使用sigsetjmp和siglongjmp函数。
函数longjmp()通过把堆栈复位成envbuf中描述的状态进行操作(envbuf的设置是由预先调用setjmp()生成的),这样使程序重新从setjmp()调用后的下一个语句开始运行,使计算机认为从刚刚执行完setjmp()的函数。从效果上看,longjmp()函数似乎“绕”过了时间和空间(内存)回到程序的原点,不必执行正常的函数返回过程。
运用这种机制,我们就可以跳过可能会造成程序core掉的坏数据,使得程序继续运行。
1、首先,我们要使用setjmp设置从哪里重新开始运行我们的程序
2、然后,我们要设置段错误的信号捕捉函数
3、最后,在信号处理函数中使用longjmp调回到我们使用setjmp设置的位置,继续执行程序。
下面给出一段示例代码
#include <iostream>
#include <signal.h>
#include <setjmp.h>
#include <string>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
jmp_buf ebuf;//初始化堆栈
//段错误的信号处理函数
void OnSIGSEGV(int signum, siginfo_t *info, void *ptr)
{
cout << "occur bad input\n";
// 跳转到setjmp设置的位置,继续执行程序
siglongjmp(ebuf,1);
}
//一个模拟会导致段错误的函数
void func_may_cause_core(const string &line)
{
if(line == "a")
{
//send a SIGSEGV signal
pid_t pid = getpid();
kill(pid, SIGSEGV);
}
}
int main()
{
//设置信号捕捉函数
struct sigaction act;
int sig = SIGSEGV;
sigemptyset(&act.sa_mask);
act.sa_sigaction = OnSIGSEGV;
act.sa_flags = SA_SIGINFO;
if(sigaction(sig, &act, NULL)<0)
{
perror("sigaction:");
}
//设置程序重新开始执行的位置
sigsetjmp(ebuf);
string line;
while(getline(cin, line))
{
func_may_cause_core(line);
cout << "good input " << line << endl;
}
return 0;
}
输出:
b
good input b
c
good input c
d
good input d
a
occur bad input
e
good input e
a
occur bad input
f
good input f