在 C++ 中處理 SIGABRT 訊號

Jinku Hu 2023年1月30日 2020年12月31日
  1. 使用 sigaction 註冊 SIGABRT 訊號處理程式
  2. 使用訊號處理程式中的 sig_atomic_t 變數
在 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 <iostream>
#include <cstring>
#include <csignal>

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 <iostream>
#include <cstring>
#include <csignal>

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);
}
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn