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 ...
随机推荐
- 从无到有开发自己的Wordpress博客主题---主页模板
在只做完成了header和footer的模板之后,我们首先在之前Hello World的基础上做一个最简单的调用测试 //修改index.php内容如下 <?php get_header(); ...
- RESTful 服务示例
WCF服务轻量级服务,可供JS调用 返回值格式:XML.Json 工程结构: 示例代码: using System; using System.Collections.Generic; using S ...
- Oracle Sql Developer 连接 SqlServer
1.下载 jTDS - SQL Server and Sybase JDBC driver 地址:http://sourceforge.net/projects/jtds/files/ 2.运行SQL ...
- ubuntu下访问支付宝官网,安装安全控件
(1)根据支付宝提示下载安装控件的压缩包 aliedit.tar.gz (2)解压安装 (3)重启浏览器就可以了
- spring源码学习之:springAOP实现底层原理
一:springAOP底层实现是基于动态代理实现的.增强和切面,以及通知.是在动态代理生成的代理类inoke方法中调用实现 //+++++++++++++aop动态代理++++++++++++++++ ...
- push()、shift()与pop()、unshift()、splice()
1.末端的添加和移除:push()是用来在数组末端添加项,pop()在数组末端移除项: 2.前端的添加和移除:shift()在移除数组的第一个项(前端),unshift()在数组前端添加项: 3.pu ...
- 最全的Javascript编码规范(推荐)
1.嵌入规则 Javascript程序应该尽量放在.js的文件中,需要调用的时候在页面中以<script src="filename.js">的形式包含进来.Javas ...
- button和input type=button的区别及注意事项
<button>标签 定义和用法 <button>标签定义一个按钮. 在button元素内部,您可以放置内容,比如文本或图像.这是该元素与使用input元素创建的按钮之间的不同 ...
- python学习之logging
学习地址:http://blog.csdn.net/zyz511919766/article/details/25136485 首先如果我们想简要的打印出日志,可以: import logging l ...
- 参数化防SQL注入
private void AddStudent(){ string strName =txtName.Text.Trim(); string strPwd = txtPwd.Text.Trim(); ...