最近组长给我安排了一个多线程异常检测的任务,主要就是捕捉sigsegv这个信号,并进行处理,保证该线程不崩溃,环境是linux C。下面给大家分享一些学习经验。
一、Linux的signal处理机制
关于linux signal机制的基本原理,给大家分享一篇很好的帖子:https://blog.csdn.net/thanksgining/article/details/41824475 。在这个我只给大家讲几点注意的地方。
- 首先要用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 。在这里我强调几点注意事项:
- 既然是glibc提供的函数,那么也就是说只有gcc编译器能用。
- 编译时要加-rdynamic参数,否则不能打印符号信息表。
- static函数不会导出符号信息symbols,在backtrace中无效。
- backtrace的实现依赖于栈指针(fp寄存器),在gcc编译过程中任何非零的优化等级(-On参数)或加入了栈指针优化参数-fomit-frame-pointer后多将不能正确得到程序栈信息。
- 内联函数没有栈帧,它在编译过程中被展开在调用的位置。
- 尾调用优化(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这两个参数