[Linux/C++] 用 signal handler 處理 segmentation fault 並產生 core dump
專案裡有個程式不太穩定,偶爾會 crash,
一般來說它就是系統自動產生 core dump 了~
但我們想要在這種情況下,還可以做一些處理,像是顯示出掛掉的 thread ID 之類的…
在網路上找到了 How to handle SIGSEGV, but also generate a core dump 這篇文章,
也順利的依照類似的方式達成需求了,
下面是個範例,差別在於我是用 sigaction() 而不是 signal()。
1. 首先要設定 signal handler
通常是在程式開始的地方設定,不然 signal 發生時還沒設定好,就接不到了。
像我的程式是一個 library 的話,就要先定義 library load 和 unload 時要執行的函式:
// Define DLL onload and onunload functions void __attribute__ ((constructor)) MyLibOnLoad(); void __attribute__ ((destructor)) MyLibOnUnLoad();
接著在 load 函式中,設定 signal handler。
下面的範例中我們抓了 SIGBUS 和 SIGSEGV 這兩種常見的 crash signal:
void MyLibOnLoad() { // Register signal handler for following signals: // SIGBUS: Bus error (bad memory access) // SIGSEGV: Invalid memory reference // Here sigaction() is used instead of signal() to block other signals // until current signal returns. struct sigaction sa = {}; memset(&sa, 0, sizeof(struct sigaction)); sigemptyset(&sa.sa_mask); sa.sa_sigaction = MyLibCrashHandler; sa.sa_flags = SA_SIGINFO|SA_RESETHAND; // Reset signal handler to system default after signal triggered sigaction(SIGBUS, &sa, NULL); sigaction(SIGSEGV, &sa, NULL); }
要注意的是,我們在 sa_flags 中,有設定 SA_RESETHAND,
所以在 signal 處理之後,signal handler 會被重設成系統預設值,
這樣才有機會讓系統預設的行為(產生 core dump)執行。
2. 處理 signal
當 signal SIGBUS/SIGSEGV 發生之後,我們設定的 signal handler 就會被呼叫到。
我們可以在這裡對這錯誤做一些處理。
但因為這是 crash,所以通常我們不會做太複雜耗時的工作(有可能會被系統強制停掉)。
在下面的範例中是將 crash 的 thread ID 寫到一個檔案去:
void MyLibCrashHandler(int nSignal, siginfo_t* si, void* arg) { // Get thread id pid_t nThreadID = syscall(SYS_gettid); // Write thread id into a file FILE* pf = fopen("crash_thread_id", "wt"); if (pf != NULL) { fprintf(pf, "%d\n", nThreadID); fclose(pf); } // Send self the same signal again, so that default system signal handler is invoked, // which will generate core dump files. kill(getpid(), nSignal); }
上面程式的最後部分,用 kill 送給自己同一個 signal。
因為我們之前已經將 sa_flags 設定為 SA_RESETHAND,
所以這第二個 signal 會被系統處理,而不是用我們定義的 signal handler,
因此系統就會依循預設行為,產生 core dump 囉~
//
//