在 C++ 中處理 SIGABRT 訊號

Jinku Hu 2023年10月12日
  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 <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);
}
作者: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

DelftStack.com 創辦人。Jinku 在機器人和汽車行業工作了8多年。他在自動測試、遠端測試及從耐久性測試中創建報告時磨練了自己的程式設計技能。他擁有電氣/ 電子工程背景,但他也擴展了自己的興趣到嵌入式電子、嵌入式程式設計以及前端和後端程式設計。

LinkedIn Facebook