在 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