linux SIGSEGV 信号捕捉,保证发生段错误后程序不崩溃
在Linux中编程的时候 有时候 try catch 可能满足不了我们的需求。因为碰到类似数组越界 ,非法内存访问之类的 ,这样的错误无法捕获。下面我们介绍一种使用捕获信号实现的异常 用来保证诸如段错误之类的错误发生时程序不会崩溃,而是跳过代码继续执行。首先我们来看看发生段错误之后系统的处理。
发生段错误后系统会抛出 SIGSEGV 信号 ,之后 调用默认的信号处理函数 ,产生core文件 ,然后关闭程序 。
那有没有一种办法可以保证程序不会死掉呢,当然是有的 。首先我们想到的是 截获改信号,调用自己的信号处理函数 。
让我们来看看signal 这个函数 。
#include <signal.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
//信号处理函数
void recvSignal(int sig)
{
printf("received signal %d !!!\n",sig);
}
int main(int argc,char** argv)
{
//给信号注册一个处理函数
signal(SIGSEGV, recvSignal);
int* s = ;
(*s) = ;
//以上两句用来产生 一个 传说中的段错误
while()
{
sleep();
printf("sleep 1 \n");
}
return ;
}
int setjmp(jmp_buf env); 这个函数 将上下文 ,就是cpu和内存的信息保存到env中 (不用去理解 jmp_buf,就当我们平时用的buff好了),然后调用 void longjmp(jmp_buf env, int val); 的时候 跳转到使用env中的信息 ,恢复上下文 。如果是第一回调用setjmp 它会返回 0,如果是在 从longjmp 跳转过来的 ,那就返回 longjmp的参数 val,根据setjmp的返回值 我们就可以决定执行可能发生错误的代码还是直接跳过这段代码 。知道了原理之后 我们可能就会这样写
#include <signal.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
jmp_buf env;
//信号处理函数
void recvSignal(int sig)
{
printf("received signal %d !!!\n",sig);
longjmp(env,);
}
int main(int argc,char** argv)
{ //保存一下上下文
int r = setjmp(env);
if( r == )
{
//初次执行 ,那么可以执行 可能会发生错误的代码
//给信号注册一个处理函数
signal(SIGSEGV, recvSignal);
printf("excute this code!!");
int* s = ;
(*s) = ;
}
else
{
//是由longjmp 跳转回来的
printf("jump this code !!");
}
while()
{
sleep();
printf("sleep 1 \n");
}
return ;
}
编译 ,执行 产生 SIGSEGV 信号 ,然后在信号函数 里边跳转 到 int r = setjmp(env); 这一行 ,之后 直接略过了 可能发生错误的这段代码 ,跳转生效,可是这种方式还有一个bug,我们看看下面的代码
#include <signal.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
jmp_buf env;
//信号处理函数
void recvSignal(int sig)
{
printf("received signal %d !!!\n",sig);
longjmp(env,);
}
int main(int argc,char** argv)
{ for(int i = ; i < ; i++)
{
//保存一下上下文
int r = setjmp(env);
if( r == )
{
//初次执行 ,那么可以执行 可能会发生错误的代码
//给信号注册一个处理函数
signal(SIGSEGV, recvSignal);
printf("excute this code!!");
int* s = ;
(*s) = ;
}
else
{
//是由longjmp 跳转回来的
printf("jump this code !!");
}
sleep();
} while()
{
sleep();
printf("sleep 1 \n");
}
return ;
}
#include <signal.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
// jmp_buf env;
//信号处理函数
void recvSignal(int sig)
{
printf("received signal %d !!!\n",sig);
siglongjmp(env,);
}
int main(int argc,char** argv)
{ for(int i = ; i < ; i++)
{
//保存一下上下文
int r = sigsetjmp(env,);
if( r == )
{
//初次执行 ,那么可以执行 可能会发生错误的代码
//给信号注册一个处理函数
signal(SIGSEGV, recvSignal);
printf("excute this code!!");
int* s = ;
(*s) = ;
}
else
{
//是由longjmp 跳转回来的
printf("jump this code !!");
}
sleep();
} while()
{
sleep();
printf("sleep 1 \n");
}
return ;
}
编译后 运行 。按照我们的需求 第二次进入for循环时, 发生段错误后程序不会死掉 ,而是会跳过这段代码了继续往下走 。下面我做了一个简单的封装 ,在错误发生时,我打印出了 错误信息 ,然后跳过错误的代码
/*
** file name CException.h
*/
#ifndef _CEXCEPTION_H_
#define _CEXCEPTION_H_
#include <setjmp.h>
#include <stdlib.h>
#include <stdarg.h>
#include <execinfo.h>
#include <stdio.h>
#include <signal.h>
#include <iostream>
#include <string.h>
typedef struct Except_frame
{
jmp_buf env;
int flag;
void clear()
{
flag = ;
bzero(env,sizeof(env));
}
bool isDef()
{
return flag;
}
Except_frame()
{
clear();
}
}Except_frame;
extern Except_frame* except_stack;
extern void errorDump();
extern void recvSignal(int sig);
Except_frame* except_stack = new Except_frame;
void errorDump()
{
const int maxLevel = ;
void* buffer[maxLevel];
int level = backtrace(buffer, maxLevel);
const int SIZE_T = ;
char cmd[SIZE_T] = "addr2line -C -f -e ";
char* prog = cmd + strlen(cmd);
readlink("/proc/self/exe", prog, sizeof(cmd) - (prog-cmd)-);
FILE* fp = popen(cmd, "w");
if (!fp)
{
perror("popen");
return;
}
for (int i = ; i < level; ++i)
{
fprintf(fp, "%p\n", buffer[i]);
}
fclose(fp);
} void recvSignal(int sig)
{
printf("received signal %d !!!\n",sig);
errorDump();
siglongjmp(except_stack->env,);
}
#define TRY \
except_stack->flag = sigsetjmp(except_stack->env,);\
if(!except_stack->isDef()) \
{ \
signal(SIGSEGV,recvSignal); \
printf("start use TRY\n");
#define END_TRY \
}\
else\
{\
except_stack->clear();\
}\
printf("stop use TRY\n");
#define RETURN_NULL \
} \
else \
{ \
except_stack->clear();\
}\
return NULL;
#define RETURN_PARAM { \
except_stack->clear();\
}\
return x;
#define EXIT_ZERO \
}\
else \
{ \
except_stack->clear();\
}\
exit();
#endif
另外建一个文件 ,
#include "CException.h"
int main(int argc,char** argv)
{
//可以如下使用
TRY
int*s = ;
(int*s) = ;
END_TRY
//使用这两个宏包含可能发生的错误代码 ,当然可以根据需求 使用
//RETURN_NULL
//RETURN_PARAM(0)
//EXIT_ZERO 这三个宏
return ;
}
这个时候我们就能使用TRY 和 END_TRY,RETURM_NULL,RETURN_PARAM(param) 来实现程序发生段错误后跳过错误代码继续运行了 ,不过此代码仅限于单线程使用。
from:http://blog.csdn.net/work_msh/article/details/8470277
linux SIGSEGV 信号捕捉,保证发生段错误后程序不崩溃的更多相关文章
- VSCODE调试时在cygwin.S中发生段错误
起因: C++实现矩阵类和向量类 当看了我实现的矩阵类后,一个同学问我: 然后我就试了试1000维,结果运行时在cygwin.S里引发了奇奇怪怪的Segmentation fault,而且这个文件还是 ...
- Linux之nohup命令:实现退出终端后程序继续后台运行
转自:http://tech.ccidnet.com/art/302/20070618/1115599_1.html 简单而有用的nohup命令在UNIX/LINUX中,普通进程用&符号放到后 ...
- Linux环境下段错误的产生原因及调试方法小结(转)
最近在Linux环境下做C语言项目,由于是在一个原有项目基础之上进行二次开发,而且 项目工程庞大复杂,出现了不少问题,其中遇到最多.花费时间最长的问题就是著名的“段错误”(Segmentation F ...
- Linux环境下段错误的产生原因及调试方法小结
转载自http://www.cnblogs.com/panfeng412/archive/2011/11/06/2237857.html 最近在Linux环境下做C语言项目,由于是在一个原有项目基础之 ...
- Linux下的段错误(Segmentation fault)
Linux开发中常见段错误问题原因分析 1 使用非法的内存地址(指针),包括使用未经初始化及已经释放的指针.不存在的地址.受系统保护的地址,只读的地址等,这一类也是最常见和最好解决的段错误问题,使用G ...
- 【转】【调试技巧】Linux环境下段错误的产生原因及调试方法小结
本文转自:http://www.cnblogs.com/panfeng412/archive/2011/11/06/segmentation-fault-in-linux.html 1. 段错误是什么 ...
- Linux环境下段错误的产生原因及调试方法小结【转】
转自:http://www.cnblogs.com/panfeng412/archive/2011/11/06/2237857.html 最近在Linux环境下做C语言项目,由于是在一个原有项目基础之 ...
- Linux段错误及GDB Coredump调试方法
最近在Linux环境下做C语言项目,由于是在一个原有项目基础之上进行二次开发,而且项目工程庞大复杂,出现了不少问题,其中遇到最多.花费时间最长的问题就是著名的“段错误”(Segmentation Fa ...
- 转:Linux环境下段错误的产生原因及调试方法小结
源地址:http://www.cnblogs.com/panfeng412/archive/2011/11/06/2237857.html 补充:http://baike.baidu.com/link ...
随机推荐
- [置顶]
【机器学习PAI实践四】如何实现金融风控
(本文数据为虚构,仅供实验) 一.背景 本文将针对阿里云平台上图算法模块来进行实验.图算法一般被用来解决关系网状的业务场景.与常规的结构化数据不同,图算法需要把数据整理成首尾相连的关系图谱.图算法更多 ...
- js实现显示系统时间的表盘
核心: 1,document.styleSheets 修改css里的keyframes属性值 2,表针角度的计算 最终显示效果: <!DOCTYPE html> <html> ...
- 程序员有什么办法能快速梳理java知识点?有这八张图就够了
一图胜千言,下面图解均来自Program Creek 网站的Java教程,目前它们拥有最多的票选.如果图解没有阐明问题,那么你可以借助它的标题来一窥究竟. 1.字符串不变性 下面这张图展示了这段代码做 ...
- 重温CLR(八 ) 泛型
熟悉面向对象编程的开发人员都深谙面向对象的好处,其中一个好处是代码重用,它极大提高了开发效率.也就是说,可以派生出一个类,让他继承基类的所有能力.派生类只需要重写虚方法,或添加一些新方法,就可定制派生 ...
- 【经典】Noip动态规划
一.线性动态规划 最长严格上升子序列 #include<iostream> #include<cstdio> using namespace std; int n,ans; ] ...
- break、continue与return的区别
1. break break语句的使用场合主要是switch语句和循环结构.在循环结构中使用break语句,如果执行了break语句,那么就退出循环,接着执行循环结构下面的第一条语句.如果在多重嵌套循 ...
- haproxy中两个常用的宏
list_for_each(pos, head) pos.head 都为list类型 一般来说,head是list的虚拟头,依次取list上的各个节点 container_of(ptr, type, ...
- Ambari的API调用
GET api/v1/clusters/HDP/configurations可以获得所有的配置信息(例如,http://hdp0:8080/api/v1/clusters/HDP/configurat ...
- (转)【Android】获取Mac地址【2】
[Android]获取Mac地址[2] 之前写了[Android]获取Mac地址[1]有些不够详细,现在贴上一些其他代码,仅供参考. (1) 调用android 的API: NetworkInterf ...
- Python学习之异常处理
1.首先了解错误和异常的概念: 错误:代码运行前的代码错误或者是程序执行过程中的逻辑错误 1:语法错误:代码不符合解释器或者编译器语法(代码错误) 2:逻辑错误:不完整或者不合法输入或者计算出现问题( ...