【整理】Linux C 段错误处理方法

  最近组长给我安排了一个多线程异常检测的任务,主要就是捕捉sigsegv这个信号,并进行处理,保证该线程不崩溃,环境是linux C。下面给大家分享一些学习经验。

一、Linux的signal处理机制

  关于linux signal机制的基本原理,给大家分享一篇很好的帖子:https://blog.csdn.net/thanksgining/article/details/41824475 。在这个我只给大家讲几点注意的地方。

  1. 首先要用signal函数进行注册,表示要捕捉哪种信号,捕捉到信号后我要怎么处理。如我要捕获SIGSEGV,见如下代码:
#include <signal.h>
......
void when_sigsegv(int sig)
......
int main()
{
    ......
    signal(SIGSEGV, when_signal);
    ......
}

  当捕获到SIGSEGV信号时,就会跳转到when_sigsegv这个函数中

 2.信号处理函数必须是void类型的,参数可以省略,或者有一个int型参数,而且该int型参数是系统写死的,不可更改,即signum.h中定义的宏,SIGSEGV是11。所以,我们是无法向处理函数中传递参数的,只能依靠全局变量。

二、比goto 强大的系统跳转函数

  按照刚才讲的,当出现段错误时,跳转到了处理函数,那么我该怎么处理,才能保证线程不崩溃呢?这就要靠setjmp和longjmp这两个强大的函数了,详解请参考这篇帖子:https://www.cnblogs.com/jiu0821/p/7207082.html
  已经有大佬总结了,我也没别要再班门弄斧。

三、获取函数调用栈

  有时我们想知道线程到底是在哪里崩溃的,即想知道具体的内存地址,此时我们可以借助glibc提供的backtrace函数。详解请见这位大佬的帖子:https://blog.csdn.net/jxgz_leo/article/details/53458366 。在这里我强调几点注意事项:

  1. 既然是glibc提供的函数,那么也就是说只有gcc编译器能用。
  2. 编译时要加-rdynamic参数,否则不能打印符号信息表。
  3. static函数不会导出符号信息symbols,在backtrace中无效。
  4. backtrace的实现依赖于栈指针(fp寄存器),在gcc编译过程中任何非零的优化等级(-On参数)或加入了栈指针优化参数-fomit-frame-pointer后多将不能正确得到程序栈信息。
  5. 内联函数没有栈帧,它在编译过程中被展开在调用的位置。
  6. 尾调用优化(Tail-call Optimization)将复用当前函数栈,而不再生成新的函数栈,这将导致栈信息不能正确被获取。

四、段错误监测并处理示例代码。(多线程)

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <setjmp.h>
#include <pthread.h>
//#include <execinfo.h>

#define MAX 10

void when_sigsegv();
void *thread(void *arg);
void do_backtrace();

jmp_buf env[MAX];
long m[MAX] = {0};

int main()
{
    pthread_t thd[MAX];
    int i;
    
    signal(SIGSEGV, when_sigsegv);
    
    for(i = 0; i < MAX ; i++)
    {
        pthread_create(&thd[i], NULL, (void *)thread, (void *)(uint64_t)i);
    }
    
    sleep(1);
    
    while(1)
    {
        printf("--------------------");
    }
    
    return 0;
}

void when_sigsegv()
{
    do_backtrace();
    int i = find_index((long)pthread_self());
    printf("sigsegv...%d...\n", i);
    siglongjmp(env[i], MAX);
}

void *thread(void *arg)
{
    int r = sigsetjmp(env[(int)(uint64_t)arg],MAX);
    if(r == 0)
    {
        printf("This is %d thread, id is %x\n", arg, pthread_self());
        m[(int)(uint64_t)arg] = (long)pthread_self();
        int *s = 0;
        (*s) = 1;  //向0地址写1,产生段错误
    }
    else
    {
        
    }
    sleep(1);
}

int find_index(long f)
{
    int i;
    for(i = 0; i < MAX; i++)
    {
        if(m[i] == f)
            return i;
    }
    
    printf("System Error,Found none!");
    
    return -1;
}

void do_backtrace()
{
    void *array[100];
    char **strings;
    int size, i;
    
    size = backtrace(array, 100);
    strings = backtrace_symbols(array, size);
    
    printf("%p\n", strings);
    for(i = 0; i < size; i++)
        printf("sigsegv at :%p:%s\n", array[i], strings[i]);
    
    free(strings);
}
    

注意编译命令,加上-rdynamic和-lpthread这两个参数

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值