在 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);
}