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 ...
 
随机推荐
- volatile与const综合分析
			
在C/C++ 编程中,volatile与const关键字一向容易让人困惑,当然,新手可能从来不用,但是 在高质量和稳健的程序中,这两个关键字 是相当重要的. 相比const,volatile关键字的发 ...
 - SITP & Raspberry Pi
			
系统安装 系统选择树莓派论坛提供的下载地址 Download 我选择了其中的SSH-2017-01-11-raspbian-jessie.zip[良心推荐] 选择一个大于8G的内存卡,利用 Win32 ...
 - [转载] C++中this指针的用法详解
			
摘自:http://blog.chinaunix.net/uid-21411227-id-1826942.html 1. this指针的用处: 一个对象的this指针并不是对象本身的一部分,不会影响s ...
 - 2017-2018-2 20179215《网络攻防实践》seed缓冲区溢出实验
			
seed缓冲区溢出实验 有漏洞的程序: /* stack.c */ /* This program has a buffer overflow vulnerability. */ /* Our tas ...
 - iOS AnchorPoint 引起的坐标问题
			
这里主要讨论设置AnchorPoint 改变时,会影响我们预期的布局问题: 一.初始代码布局 //参照页面 UIView *aView = [[UIView alloc]initWit ...
 - jQuery的ajax跨域实现
			
今天有人问我跨域ajax请求是否可以发送,之前没接触过此类问题,没答上,后来查了下,以下备忘. 我在本地建了三个站点,并设置了host文件模拟跨子域和跨全域 coolkissbh.com blog.c ...
 - bzoj 4650 & 洛谷 P1117 优秀的拆分 —— 枚举关键点+后缀数组
			
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4650 https://www.luogu.org/problemnew/show/P1117 ...
 - 提升Apache网站访问速度的优化方法
			
Apache是世界使用排名第一的Web服务器软件.它可以运行在几乎所有广泛使用的计算机平台上,由于其跨平台和安全性被广泛使用,是最流行的Web服务器端软件之一. 在Apache服务器上怎样优化才能提高 ...
 - springboot mybatisPlus配置
			
1.pom依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="htt ...
 - optimize table tablename
			
optimize 优化表OPTIMIZE 命令支持的引擎MyIsam, InnoDB, ARCHVE当对表有大量的增删改操作时,需要用optimize对表进行优化,可以减少空间与提高I/O性能,命令o ...