Linux C信号编程:接管段错误信号SIGSEGV打印调用栈

有时候,线上环境没有开启coredump,这时把进程复位时的调用栈打印出来就非常有利于问题定位了。怎么做呢?也很简单,我们在程序中接管SGISEGV信号,在信号处理函数打印调用栈信息即可。

信号处理可以看:UNIX环境高级编程
内核机制可以看:Linux内核源代码情景分析
这两本书虽老了点,但写的确实很不错,相关系统API使用可以配合可能Linux man手册。

接管段错误信号后,在信号处理函数中打印当前调用栈即可。此时的栈信息可以回溯到之前发生异常时的函数栈帧。整个过程大致是这样的:用户太程序在运行,堆栈寄存器指向用户栈,访问非法内存,中断陷入内核,内核掉注册的信号处理函数,又切回到了用户态复用之前的栈。理解压栈原理,很容易从当前栈基址往前回溯整个站的,原理可以看:Linux C函数调用栈帧结构。并且,Linux也有API可以直接调用来打印调用栈。当然,如果栈内存被破坏了,就还原不出来了。

一个例子如下,记得编译的时候加上 -rdynamic 参数,可以打出符号名称。代码非常简单:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <execinfo.h>
#include <unistd.h>

void ShowStack()
{
    int i;
    void *buffer[1024];
    int n = backtrace(buffer, 1024);
    char **symbols = backtrace_symbols(buffer, n);
    for (i = 0; i < n; i++) {
        printf("%s\n", symbols[i]);
    }
}

void SigSegvProc(int signo) {
    if (signo == SIGSEGV) {
        printf("Receive SIGSEGV signal\n");
        printf("-----call stack-----\n");
        ShowStack();
        exit(-1);
    } else {
        printf("this is sig %d", signo);
    }
}

void RegSig() {
    signal(SIGSEGV, SigSegvProc);
}

void fun3() {
    printf("this is fun3\n");
    *(char*)0 = 1; // read nullptr
}

void fun2() {
    printf("this is fun2\n");
    fun3();
}

void fun1() {
    printf("this is fun1\n");
    fun2();
}

int main() {
    RegSig();
    fun1();
    return 0;
}

其运行结果如下:

➜  sig gcc -g -rdynamic main.c
➜  sig ./a.out
this is fun1
this is fun2
this is fun3
Receive SIGSEGV signal
-----call stack-----
./a.out(ShowStack+0x2e) [0x55d6574ebb38]
./a.out(SigSegvProc+0x33) [0x55d6574ebbec]
/lib/x86_64-linux-gnu/libc.so.6(+0x3ef20) [0x14a39f418f20]
./a.out(fun3+0x15) [0x55d6574ebc3c]
./a.out(fun2+0x1a) [0x55d6574ebc5c]
./a.out(fun1+0x1a) [0x55d6574ebc79]
./a.out(main+0x18) [0x55d6574ebc94]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x14a39f3fbb97]
./a.out(_start+0x2a) [0x55d6574eba2a]

如果没有开coredump,那么不妨就自己接管段错误SIGSEGV信号,打印非法访问内存时的调用栈。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值