跳到主要内容

C++ 信号处理

信号(signal)是一种进程间通信机制,它给应用程序提供一种异步的软件中断,使应用程序有机会接受其他程序或终端发送的命令(即信号)。

信号

应用程序收到信号后,有三种处理方式:忽略,默认,或捕捉。

进程收到一个信号后,会检查对该信号的处理机制。

  • 如果是 SIG_IGN,就忽略该信号;
  • 如果是 SIG_DFT,则会采用系统默认的处理动作,通常是终止进程或忽略该信号;
  • 如果给该信号指定了一个处理函数(捕捉),则会中断当前进程正在执行的任务,转而去执行该信号的处理函数,返回后再继续执行被中断的任务。

在 UNIX、Linux、Mac OS X 或 Windows 系统上,可以通过按 Ctrl+C 产生中断。

信号列表

有些信号不能被程序捕获,但是下表所列信号可以在程序中捕获,并可以基于信号采取适当的动作。这些信号是定义在 C++ 头文件 <csignal> 中。

信号描述
SIGABRT程序的异常终止,如调用 abort。
SIGFPE错误的算术运算,比如除以零或导致溢出的操作。
SIGILL检测非法指令。
SIGINT接收到交互注意信号。
SIGSEGV非法访问内存。
SIGTERM发送到程序的终止请求。

signal 函数

C++ 信号处理库 <csignal> 提供了 signal() 函数,用来捕获突发事件。函数原型如下:

void (*signal (int sig, void (*func)(int)))(int);

这个函数接收两个参数:

  • 第一个参数 sig 是一个整数,代表了信号的编号;
  • 第二个参数 func 是一个指向信号处理函数的指针。

不管在程序中捕获什么信号,都必须使用 signal() 函数来注册信号,并将其与信号处理程序相关联。

示例 : 使用 signal() 函数捕获 SIGINT 信号

#include <iostream>
#include <csignal>

#include <unistd.h>

using namespace std;

void signalHandler(int signum)
{
cout << "Interrupt signal (" << signum << ") received.\n";
exit(signum);
}

int main(void)
{
signal(SIGINT, signalHandler); // 注册信号 SIGINT 和信号处理程序

while (1) {
cout << "Going to sleep...." << endl;
sleep(1);
}

return 0;
}

编译运行以上示例,输出结果如下:

Going to sleep....
Going to sleep....
Going to sleep....

此时按下组合键 Ctrl+C 中断程序,会看到程序捕获信号,程序打印如下内容并退出:

Going to sleep....
Going to sleep....
Going to sleep....
^CInterrupt signal (2) received.

raise 函数

raise() 函数用于生成信号,函数原型如下:

int raise (signal sig);

参数 sig 是要发送的信号的编号,这些信号包括 SIGINTSIGABRTSIGFPESIGILLSIGSEGVSIGTERMSIGHUP 等。

示例:使用 raise() 函数内部生成信号

#include <iostream>
#include <csignal>
#include <unistd.h>

using namespace std;

void signalHandler(int signum)
{
cout << "Interrupt signal (" << signum << ") received.\n";
exit(signum);
}

int main(void)
{
int i = 0;

signal(SIGINT, signalHandler); // 注册信号 SIGINT 和信号处理程序

while (++i) {
cout << "wait for 1s...." << endl;
if (i == 3) {
raise(SIGINT);
}
sleep(1);
}

return 0;
}

编译运行以上程序,输出结果如下:

wait for 1s....
wait for 1s....
wait for 1s....
Interrupt signal (2) received.