天天看點

優雅處理段錯誤

摘要:某些程序在結束前必須要處理一些額外的過程才能結束,尤其是資料存儲的子產品,程序停止前為保證資料的完整性可能要做一些事情,如果發生段錯誤,這時就需要先截獲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了事

說的比較亂,直接看代碼吧

#include <stdint.h>

#include <stdio.h>

#include <pthread.h>

#include <stdlib.h>

#include <unistd.h>

#include <signal.h>

#include <string.h>

volatile bool begin_segv_handle=false;

volatile bool already_handle_other_function=false;

void segv_handler(int signo);

void * core_thread(void *args) {

        int i=0;

        while(1) {

                printf("in core thread\n");

                sleep(1);

                if(i++>2) {

                        strcpy(NULL,"abc");

                }

        }

        return NULL;

}

void * other_function_thread(void *args) {

                printf("in other_function_thread\n");

                if(begin_segv_handle) {

                        printf("set already_handle_other_function\n");

                        already_handle_other_function = true;

                        break;

void segv_handler(int signo) {

        printf("in segv_handler\n");

        if(begin_segv_handle) {

                signal(SIGSEGV,SIG_DFL);

                return ;

        begin_segv_handle = true;

                if(i++>10) {

                if(already_handle_other_function){

                        break;                

        signal(SIGSEGV,SIG_DFL);

int main(void)

{

        signal(SIGSEGV,segv_handler);

        pthread_t tid_core_thread=0UL,tid_other_function_thread=0UL;

        pthread_create(&tid_core_thread,NULL,core_thread,NULL);

        pthread_create(&tid_other_function_thread,NULL,other_function_thread,NULL);

        pthread_join(tid_core_thread,NULL);

        pthread_join(tid_other_function_thread,NULL);

        return 0;

繼續閱讀