优雅处理段错误

摘要:某些进程在结束前必须要处理一些额外的过程才能结束,尤其是数据存储的模块,进程停止前为保证数据的完整性可能要做一些事情,如果发生段错误,这时就需要先截获segv信号,处理完后再让程序出core
一般进程收到段错误信号默认是dump core文件然后退出,但有些进程在退出时需要处理额外的过程才能结束,这时就不能让信号执行默认的动作了,我们就需要截获段错误信号,然后在信号处理函数中
处理额外过程,我们称之为other_function,但是我们处理完后其实还是需要让程序core出来,以便知道是哪儿出问题了。
基础知识预备:
1.对线程而言,有三种信号类型:异步信号,同步信号,定向信号。其中异步信号是指传递给某些解除了对该信号的阻塞的线程的信号,同步信号是指传递给引发该信号的线程的信号,定向信号是指由pthread_kill函数发送给指定线程的信号。像SIGSEGV(段错误信号),SIGPFE(浮点错误)这样的错误信号就是同步于引发他们的线程的,因为引发这些信号的线程将等待信号处理程序处理完成后才能继续进行,这样的信号只能由本线程处理,而其他信号因为不与特定的线程相关,所以他们是异步的,例如其他进程给本进程发送的信号。如果有几个线程都解除了对同一个异步信号的阻塞,当有信号到达时,线程运行系统就从中选取一个来处理。
2.对于SIGSEGV信号,如果是由进程段错误导致的,则只能设定信号处理函数,不能阻塞或忽略,如果有是由别的进程发送的,则可以阻塞或忽略。
我们考虑了大概三个方案:
方案1:直接设置信号处理函数,在信号处理函数中处理other_function,处理完后再signal(SIGSEGV,SIG_DFL),这看起来比较完美,但实际上有锁的问题。如果出现段错误的线程中使用了锁,在没有解锁之前发生段错误,other_function中也使用了同一个锁,则容易出现死锁,造成进程hang住。other_function中无锁时此方案比较简单易行。
方案2:其他线程阻塞信号,由专有线程处理段错误信号。这个不可行,因为我们之前说了,段错误信号如果由进程产生则不能被阻塞或忽略
方案3:直接设置信号处理函数,但使用专有线程处理other_function。接到段错误信号后调用信号处理函数,信号处理函数中设置开始处理标记,然后定期检查专有线程是否处理完,专有线程定期检查开始处理标记,发现设定了就开始处理other_function,处理完之后设置处理完标记,然后推出。信号处理函数中定期检查处理完标记,发现一旦被处理完了则再signal(SIGSEGV,SIG_DFL),然后退出,等待出core。
这个方案也会出现方案1中的死锁问题,但是此时的死锁是两个线程之间的死锁,而不是同一个线程的死锁,所以是可以处理的,信号处理函数中设置一个超时时间即可,即信号处理函数发现专有线程太久没有处理完other_function就知道出现死锁了,这时没辙了,直接signal(SIGSEGV,SIG_DFL),让程序出core完事,由于这种可能是比较少的,所以是可以接受的。
另外一个问题是:如果other_function中本身也出core就比较囧了,此时可以在信号处理函数中做判断,如果开始处理标记已设定,说明已经已经在处理core了,再进来就直接signal(SIGSEGV,SIG_DFL),让程序出core了事
说的比较乱,直接看代码吧

  1. #include <stdint.h>
  2. #include <stdio.h>
  3. #include <pthread.h>
  4. #include <stdlib.h>
  5. #include <unistd.h>
  6. #include <signal.h>
  7. #include <string.h>
  8. volatile bool begin_segv_handle=false;
  9. volatile bool already_handle_other_function=false;
  10. void segv_handler(int signo);
  11. void * core_thread(void *args) {
  12.         int i=0;
  13.         while(1) {
  14.                 printf("in core thread ");
  15.                 sleep(1);
  16.                 if(i++>2) {
  17.                         strcpy(NULL,"abc");
  18.                 }
  19.         }
  20.         return NULL;
  21. }
  22. void * other_function_thread(void *args) {
  23.         int i=0;
  24.         while(1) {
  25.                 printf("in other_function_thread ");
  26.                 if(begin_segv_handle) {
  27.                         printf("set already_handle_other_function ");
  28.                         already_handle_other_function = true;
  29.                         break;
  30.                 }
  31.                 sleep(1);
  32.         }
  33.         return NULL;
  34. }
  35. void segv_handler(int signo) {
  36.         printf("in segv_handler ");
  37.         if(begin_segv_handle) {
  38.                 signal(SIGSEGV,SIG_DFL);
  39.                 return ;
  40.         }
  41.         begin_segv_handle = true;
  42.         int i=0;
  43.         while(1) {
  44.                 if(i++>10) {
  45.                         break;
  46.                 }
  47.                 if(already_handle_other_function){
  48.                         break;                
  49.                 }
  50.                 sleep(1);
  51.                 
  52.         }
  53.         signal(SIGSEGV,SIG_DFL);
  54. }
  55. int main(void)
  56. {
  57.         signal(SIGSEGV,segv_handler);
  58.         pthread_t tid_core_thread=0UL,tid_other_function_thread=0UL;
  59.         pthread_create(&tid_core_thread,NULL,core_thread,NULL);
  60.         pthread_create(&tid_other_function_thread,NULL,other_function_thread,NULL);
  61.         pthread_join(tid_core_thread,NULL);
  62.         pthread_join(tid_other_function_thread,NULL);
  63.         return 0;
  64. }
原文地址:https://www.cnblogs.com/lidabo/p/4548894.html