利用linux信号机制调试段错误(Segment fault)【转】

简介: 转自:http://blog.csdn.net/ab198604/article/details/6164517 版权声明:本文为博主原创文章,未经博主允许不得转载。 在实际开发过程中,大家可能会遇到段错误的问题,虽然是个老问题,但是其带来的隐患是极大的,只要出现一次,程序立即崩溃中止。

转自:http://blog.csdn.net/ab198604/article/details/6164517

版权声明:本文为博主原创文章,未经博主允许不得转载。

        在实际开发过程中,大家可能会遇到段错误的问题,虽然是个老问题,但是其带来的隐患是极大的,只要出现一次,程序立即崩溃中止。如果程序运行在PC中,segment fault的调试相对比较方便,因为可以通过串口、显示器可以查看消息,只要程序运行,通过GDB调试工具即可捕捉产生segment fault的具体原因。但是不知大家有没有想法,当程序运行在嵌入式设备上时,你所面临资源的缺乏,你没有串口打印信息,没有显示器可查看,你不知道程序运行的状态,如果程序的产生segment falut这种bug发生的周期1年之内只发生过三四次,时间又不确定,你又如何调试知道程序发生错误的具体位置呢?等等问题实在令人纠结。

        解决此问题的方式方法有如下几种:

       1 在产品发布之前,尽量将所有segment fault产生原因找出,全部消除(最理想情况下)

       2 在程序中的关键位置增加打印信息,设定打印级别,通过打信的信息缩小查bug的范围,如果是嵌入式设备,需要将这些信息写入文件,保存在flash中。

       3 利用看门狗喂狗,如果程序中止或长期陷入死循环,将重新加载程序

       4 利用Linux信号机制来解决段错误问题(有点类似于软看门狗)

 

 

       下面我着重讲解第4要点的解决方式:

        首先,我先叙述下我的总体思路,假设我的程序在某处调用一函数dummy_func(),这个函数有个segment fault段错误,如果你程序没对其进行处理,不用怀疑,你的程序马上挂掉,如果是嵌入式设备里的程序,你可能不知道产生segment fault的具体原因和具体位置,只能看log日志慢慢分析。我们知道,在linux的信号机制中,当产生segment fault错误时,程序会产生SIGSEGV信号,于是我们试想,如果我们在程序中能够及时捕捉到此信号,然后在此信号处理函数中重新加载此应用程序,就可以实现类似看门狗的功能,暂且将这种方法称之为软件看门狗吧。但是此方法只是权宜之计,它可以让你的产品在客户面前保持良好印象(因为如果程序挂掉后又重新加载了,而客户不知道),而不至于长期很频繁的去现场解决此问题,这种方式适用于产生段错误的周期具有不确定性,其实我们都知道遇到segment fault我们还得解决此问题产生的具体原因,这才是正确的解决方式。

        下面我列出完整的程序源码,虽然简单,但是非常有用:

 

/******************************程序开始 **********************************/

 

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>

char main_status = 0;

/***********************
 * 此函数产生一个段错误
 * *********************/
void dummy_func(void)
{
    printf("hello world/n");
    char *p = NULL;     //0地址
    *p = 0x1a;             //对0地址写入数据,出现段错误
    return;
}



/************************
 * 此函数用于重新加载程序
 ************************/
void deal(void)
{
   char buffer[255];
   memset(buffer, 0, sizeof(buffer));
   sprintf(buffer, "cd ~/test");
   system(buffer);

   /*
    *此延时很重要,如果不加延时,ctrl+c的信号无法及时处理(即ctrl+c失效),程序将循环加载   
    * 如果不加延时,程序又重新执行后面./test1语句将重新加载应用程序了
     */
     sleep(5); 

   memset(buffer, 0, sizeof(buffer));
   sprintf(buffer, "./test1");
   system(buffer);
   printf ("xxxxxxxxxxxxxxx/n");
   
   if( 1 == main_status )
        exit(0);
}


/**************************
 * 捕捉到ctrl+c信号的处理函数
 **************************/
void ctrl_c_func(int signo)
{
    printf("stop the demo/n");
    main_status = 1;  //置标志位
    exit(0);
}


int ctrl_c_func_init(void)
{
    int ret = 0;
    struct sigaction act;

    act.sa_handler = ctrl_c_func;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;

    ret = sigaction(SIGINT, &act, NULL);
}



/****************************
 * main主程序
 ****************************/
int main(int argc, char **argv)
{
    signal(SIGSEGV, &deal);   //捕捉SIGSEGV信号
    signal(SIGINT, &ctrl_c_func);  //捕捉SIGINT信号
//    ctrl_c_func_init();  //如果不用上面捕捉,调用这个函数也行

    while(1)
    {
        if( 1 == main_status )  //如果接收到标志位,则退出
        {
             exit(0);
        }
        dummy_func();  //调用产生segment fault函数
    }
    return 0;
}
/******************************程序结束 **********************************/

 

【作者】 张昺华
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
目录
相关文章
|
7月前
|
移动开发 Ubuntu 网络协议
Linux(11)Debain 调试EC25-EUX总记录
Linux(11)Debain 调试EC25-EUX总记录
251 0
|
7月前
|
Linux
Linux(5)WIFI/BT调试笔记
Linux(5)WIFI/BT调试笔记
375 0
|
29天前
|
缓存 NoSQL Linux
Linux调试
本文介绍了Linux调试、性能分析和追踪的培训资料,涵盖调试、性能分析和追踪的基础知识及常用工具。
221 6
Linux调试
|
2月前
|
安全 NoSQL Ubuntu
linux 添加用户useradd 段错误 (核心已转储)
在Linux系统中使用`useradd`命令添加用户时遇到“段错误 (核心已转储)”的问题,通常表明存在系统级或关键组件问题。可通过检查系统日志(如`/var/log/syslog`)和内核消息来定位错误。此外,还需检查并修复文件系统错误(使用`fsck`),确保关键配置文件未损坏,并更新系统包与安全补丁。使用内存检测工具(如memtest86+)排查硬件故障,或重新安装`useradd`及其依赖库。通过`strace`跟踪命令执行过程,检查SELinux/AppArmor安全策略影响,并分析核心转储文件以进一步诊断问题根源。
174 1
|
7月前
|
网络协议 算法 Linux
【Linux】深入探索:Linux网络调试、追踪与优化
【Linux】深入探索:Linux网络调试、追踪与优化
|
4月前
|
NoSQL Linux C语言
Linux GDB 调试
Linux GDB 调试
67 10
|
4月前
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
118 3
|
4月前
|
Ubuntu Linux
内核实验(四):Qemu调试Linux内核,实现NFS挂载
本文介绍了在Qemu虚拟机中配置NFS挂载的过程,包括服务端的NFS服务器安装、配置和启动,客户端的DHCP脚本添加和开机脚本修改,以及在Qemu中挂载NFS、测试连通性和解决挂载失败的方法。
239 0
内核实验(四):Qemu调试Linux内核,实现NFS挂载
|
5月前
|
运维 Java Linux
(九)JVM成神路之性能调优、GC调试、各内存区、Linux参数大全及实用小技巧
本章节主要用于补齐之前GC篇章以及JVM运行时数据区的一些JVM参数,更多的作用也可以看作是JVM的参数列表大全。对于开发者而言,能够控制JVM的部分也就只有启动参数了,同时,对于JVM的性能调优而言,JVM的参数也是基础。
120 8
|
5月前
|
Linux C# iOS开发
如何用 WinDbg 调试Linux上的 .NET程序
【7月更文挑战第13天】 1. `dotnet-dump`: Collects process dumps with `dotnet-dump collect -p &lt;process_id&gt;`. 2. `lldb`: Debugs Mono runtime apps on macOS/Linux. 3. **Visual Studio Code**: Remotely debugs .NET via the C# extension. 4. **JetBrains Rider**: Supports remote debugging of .NET on Linux.