在 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