在 C++ 中處理 SIGABRT 訊號
本文將演示如何在 C++ 中處理 SIGABRT
訊號的多種方法。
使用 sigaction
註冊 SIGABRT
訊號處理程式
基於 Unix 的作業系統支援名為訊號的功能,這是一個程式可以非同步向另一個程式傳送訊息的機制。訊號通常是由使用者或需要中斷程式的作業系統程序發出的。訊號處理程式是程式收到給定訊號時被呼叫的程式碼部分。對於中止程式、停止程式、繼續程式等任務,有一些標準的訊號,其對應的預設訊號處理程式。但是,使用者可以用自定義的函式覆蓋這些訊號處理程式的大部分,sigaction
就是註冊它的函式。
注意,sigabrt
是預設導致程式終止的訊號之一。在接下來的程式碼示例中,我們重寫它的訊號處理程式,並指定我們定義的函式(cleanupRoutine
)在收到訊號後被呼叫。
首先,struct sigaction
型別的物件應該被宣告,並通過呼叫 memset
函式進行初始化。
接下來,應該為其資料成員 sa_handler
分配需要呼叫的函式地址。
之後,我們需要遮蔽其他訊號,以免干擾 SIGABRT
處理程式,sigfillset
函式呼叫就可以實現這一點。
最後,我們用 sigaction
呼叫來註冊訊號處理程式,它需要三個引數:訊號編號、struct sigaction
的地址,以及可選的另一個可以儲存先前動作的結構。
在這種情況下,我們忽略第三個引數,但 nullptr
仍然需要指定為引數。我們定義了 cleanupRoutine
函式,將字串列印到控制檯,然後退出程式,方便驗證處理程式的正確執行。剩下的程式就是無限迴圈,當使用者發出 SIGABRT
訊號時需要中斷。為了測試程式,在一個終端視窗中執行程式,從第二個視窗傳送訊號,執行以下命令 kill -SIGABRT pid_num_of_program
。
#include <csignal>
#include <cstring>
#include <iostream>
void cleanupRoutine(int signal_number) {
write(2, "printed from cleanupRoutine\n", 28);
_exit(EXIT_SUCCESS);
}
int main() {
struct sigaction sigabrt_action {};
memset(&sigabrt_action, 0, sizeof(sigabrt_action));
sigabrt_action.sa_handler = &cleanupRoutine;
if (sigfillset(&sigabrt_action.sa_mask) != 0) {
perror("sigfillset");
exit(EXIT_FAILURE);
}
if (sigaction(SIGABRT, &sigabrt_action, nullptr) != 0) {
perror("sigaction SIGABRT");
exit(EXIT_FAILURE);
}
int i = 0;
while (true) {
i += 1;
}
exit(EXIT_SUCCESS);
}
使用訊號處理程式中的 sig_atomic_t
變數
訊號處理程式是一種特殊型別的函式。唯一能保證正確操作的變數是原子變數。有一個特殊的型別 sig_atomic_t
,是一個整數,可以在訊號處理程式執行過程中設定。這個變數是用 volatile
關鍵字宣告的,對它的任何修改都是全域性性的。下面的例子演示了我們如何將這個變數作為迴圈語句的條件。
請注意,處理程式只將變數從 1 設定為 0,而不是在收到訊號時退出程式。意思是,程式嘗試從被中斷的點繼續。在這種情況下,迴圈迭代被重新啟動,當一旦條件被檢查為假時,就會跳出迴圈。這樣,使用者就可以利用訊號來控制程式行為。
#include <csignal>
#include <cstring>
#include <iostream>
using std::cout;
using std::endl;
volatile sig_atomic_t shutdown_flag = 1;
void cleanupRoutine(int signal_number) { shutdown_flag = 0; }
int main() {
struct sigaction sigabrt_action {};
memset(&sigabrt_action, 0, sizeof(sigabrt_action));
sigabrt_action.sa_handler = &cleanupRoutine;
if (sigfillset(&sigabrt_action.sa_mask) != 0) {
perror("sigfillset");
exit(EXIT_FAILURE);
}
if (sigaction(SIGABRT, &sigabrt_action, nullptr) != 0) {
perror("sigaction SIGABRT");
exit(EXIT_FAILURE);
}
int i = 0;
while (shutdown_flag) {
i += 1;
}
cout << "Exiting ..." << endl;
exit(EXIT_SUCCESS);
}