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 ...
随机推荐
- EasyDSS流媒体服务器出现no compatible source was found for this media问题的解决
在EasyDSS流媒体服务器的客户反馈中,我们遇到这样一个现象,在chrome中经常会出现RTMP/HLS流无法播放的问题: 这个问题复现的几率比较低,因为chrome禁止了flash的加载,这也从另 ...
- 使用python对文件中的数值进行累加
问题描述: 一个文件由若干条记录组成,记录的格式为:“num1 num2”,有时候,需要统计文件中num1对应的num2的总值.处理问题的思路 用传说中的python来处理,很方便.几行代码就可以了. ...
- 编写实现字符串拷贝函数strcpy()完整版
有个题目编程实现字符串拷贝函数strcpy(),很多人往往很快就写出下面这个代码. void strcpy( char *strDest,char *strSrc ) { while(( *strDe ...
- 深入了解zookeeper(三)
一.ZooKeeper 的实现 1.1 ZooKeeper处理单点故障 我们知道可以通过ZooKeeper对分布式系统进行Master选举,来解决分布式系统的单点故障,如图所示. 那么我们继续分析一下 ...
- Windows 系统定时自动重启
1.创建新文本并输入 shutdown -r -t 0 保存成.bat文件 2.创建系统任务计划 2.1 在开始中打开[任务计划程序] 2.2 新建创建任务计划目录 2.3 在新目录下新建任务计划即可 ...
- LeetCode IPO
原题链接在这里:https://leetcode.com/problems/ipo/description/ 题目: Suppose LeetCode will start its IPO soon. ...
- cordic算法的fpga实现
cordic算法参考:http://wenku.baidu.com/view/6c623aa8910ef12d2bf9e732.html 这是百度文库的一个文档,详细介绍了cordic算法的基本内容. ...
- ARP表 MAC表 路由表
ARP表是一个动态表,存储在计算机当中,目的是做一个ip地址与mac地址的对应.假设在同一子网段,计算机A与计算机B通信计算机A的ip地址192.168.0.1 MAC地址AA-AA-AA-AA-AA ...
- LG3648 [APIO2014]序列分割
题意 你正在玩一个关于长度为 \(n\) 的非负整数序列的游戏.这个游戏中你需要把序列分成 \(k+1\) 个非空的块.为了得到 \(k+1\) 块,你需要重复下面的操作 \(k\) 次: 选择一个有 ...
- 不得不注意tornado多进程部署的副作用
tornado多进程启动时,采用的是fork的方式. 一个现有进程可以调用fork函数创建一个新进程.由fork创建的新进程被称为子进程(child process).fork函数被调用一次但返回两次 ...