C++ Signal使用
简介
信号机制是进程之间相互传递消息的一种方法,信号全称为软中断信号,也有人称软中断。从它的命名可以看出,它的实质和使用很像中断,所有,信号可以说是进程控制的一部分。信号signal处理是Linux程序的一个特色,用信号处理来模拟操作系统的中断功能。C++使用处理信号的标准库#include <csignal>
或#include <signal.h>
1.什么是信号
#include <csignal>
或#include <signal.h>
是处理信号的C标准库,该库主要包含了signal()
与raise()
两个功能函数。- 函数signal用于捕获信号,可指定信号处理的方式。
- 函数raise产生一个信号,并向当前正在执行的程序发送该信号。
信号
signal
可以理解为由操作系统传给程序(进程)的事件,只是用来通知程序发生了什么事件,并不会传递给该进程任何数据。信号是一种中断,因为它可以改变程序的流程。当信号传递给进程时,进程将停下其正在执行的操作,并去处理或忽略该信号异步事件。
异步事件 - 查看信号的方式是一种处理异步事件的机制。
- 当程序通过signal函数捕获信号后,若signal函数第二参数为函数指针,则调用signal函数的程序会阻塞(暂停在signal函数这句),直至异步线程(进入函数指针)return。程序将从暂停点恢复执行。
- 此外,进程之间可以互相通过系统调用kill发送软中断信号。
(without define class style)
- C++头文件
signum.h
和signum-generic.h
中定义了以下信号/* We define here all the signal names listed in POSIX (1003.1-2008); as of 1003.1-2013, no additional signals have been added by POSIX. We also define here signal names that historically exist in every real-world POSIX variant (e.g. SIGWINCH). Signals in the 1-15 range are defined with their historical numbers. For other signals, we use the BSD numbers. There are two unallocated signal numbers in the 1-31 range: 7 and 29. Signal number 0 is reserved for use as kill(pid, 0), to test whether a process exists without sending it a signal. */ /* ISO C99 signals. */ #define SIGINT 2 /* Interactive attention signal. */ #define SIGILL 4 /* Illegal instruction. */ #define SIGABRT 6 /* Abnormal termination. */ #define SIGFPE 8 /* Erroneous arithmetic operation. */ #define SIGSEGV 11 /* Invalid access to storage. */ #define SIGTERM 15 /* Termination request. */ /* Historical signals specified by POSIX. */ #define SIGHUP 1 /* Hangup. */ #define SIGQUIT 3 /* Quit. */ #define SIGTRAP 5 /* Trace/breakpoint trap. */ #define SIGKILL 9 /* Killed. */ #define SIGBUS 10 /* Bus error. */ #define SIGSYS 12 /* Bad system call. */ #define SIGPIPE 13 /* Broken pipe. */ #define SIGALRM 14 /* Alarm clock. */ /* New(er) POSIX signals (1003.1-2008, 1003.1-2013). */ #define SIGURG 16 /* Urgent data is available at a socket. */ #define SIGSTOP 17 /* Stop, unblockable. */ #define SIGTSTP 18 /* Keyboard stop. */ #define SIGCONT 19 /* Continue. */ #define SIGCHLD 20 /* Child terminated or stopped. */ #define SIGTTIN 21 /* Background read from control terminal. */ #define SIGTTOU 22 /* Background write to control terminal. */ #define SIGPOLL 23 /* Pollable event occurred (System V). */ #define SIGXCPU 24 /* CPU time limit exceeded. */ #define SIGXFSZ 25 /* File size limit exceeded. */ #define SIGVTALRM 26 /* Virtual timer expired. */ #define SIGPROF 27 /* Profiling timer expired. */ #define SIGUSR1 30 /* User-defined signal 1. */ #define SIGUSR2 31 /* User-defined signal 2. */ /* Nonstandard signals found in all modern POSIX systems (including both BSD and Linux). */ #define SIGWINCH 28 /* Window size change (4.3 BSD, Sun). */ /* Archaic names for compatibility. */ #define SIGIO SIGPOLL /* I/O now possible (4.2 BSD). */ #define SIGIOT SIGABRT /* IOT instruction, abort() on a PDP-11. */ #define SIGCLD SIGCHLD /* Old System V name */
2.函数signal
函数signal 用于捕获信号,可指定信号处理的方式,函数声明如下:
void (*signal(int sig, void (*func)(int)))(int);
第一个参数 sig 指明了所要处理的信号类型,它可以取除了SIGKILL和SIGSTOP外的任何一种信号。
第二个参数描述了与信号关联的动作,下面将会对这三个参数进行讲解。
当程序收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:
1. 默认处理:对信号进行该信号的系统默认处理,第二参数为 SIG_DFL。
2. 忽略信号:忽略该信号,第二参数为 SIG_IGN。
3. Function handler:指定处理函数,由该函数来处理,第二参数为 函数指针 。
若signal函数中第二参数(函数指针)中通过 raise发出与signal函数对应信号类型相同的信号。程序将在signal函数这成为死循环,自己发信号,自己捕获,进程暂停。
2.1默认处理-SIG_DFL
SIG_DFL 对信号进行该信号的系统默认处理。
#include <chrono>
#include <iostream>
#include <csignal>
#include <thread>
using namespace std;
int main() {
signal(SIGINT, SIG_DFL);
while(1){
std::this_thread::sleep_for (std::chrono::seconds(1));
std::cout << "hello world!" << endl;
}
return 0;
}
该程序包含一个死循环,循环内以一秒为间隔 打印语句。
SIGINT信号代表由InterruptKey产生,通常是CTRL +C 。执行上述代码时,按下CTRL + C 后程序退出。
2.2忽略信号-SIG_IGN
SIG_IGN 表示忽略该信号。
#include <chrono>
#include <iostream>
#include <csignal>
#include <thread>
using namespace std;
int main() {
signal(SIGINT, SIG_IGN);
while(1){
std::this_thread::sleep_for (std::chrono::seconds(1));
std::cout << "hello world!" << endl;
}
return 0;
}
该程序包含一个死循环,循环内以一秒为间隔 打印语句。
SIGINT信号代表由InterruptKey产生,通常是CTRL +C 。执行上述代码时,按下CTRL + C 后程序没有反应。
如果我们想结束该程序可以按下CTRL +\,CTRL +\ 组合键会产生SIGQUIT信号,此信号并没有被忽略。
2.3自定义函数
void (*signal(int sig, void (*func)(int)))(int);
第二参数为 函数指针 :当signal函数捕获信号后,通过指定函数进行处理。
此函数必须在 signal() 被调用前申明。
#include <chrono>
#include <iostream>
#include <csignal>
#include <thread>
using namespace std;
using namespace std;
void signalHandler( int signum ) {
std::cout << "我来处理!" << std::endl;
std::this_thread::sleep_for (std::chrono::seconds(5));
}
int main() {
signal(SIGINT, signalHandler); // 捕获 SIGINT
std::this_thread::sleep_for (std::chrono::seconds(2));
std::cout << "程序结束." << endl;
return 0;
}
该程序声明了信号类型为 SIGINT 的signal函数,用于捕获 SIGINT信号。SIGINT信号代表由InterruptKey产生,通常是CTRL +C
当什么都不做时,程序会正常运行到结束。
如果在程序结束前按下CTRL +C ,函数捕获SIGINT信号,进入signalHandler函数,mian函数阻塞,直至signalHandler函数运行结束,mian函数打印后程序结束。
3.函数raise
函数raise 产生一个信号sig,并向当前正在执行的程序发送信号sig,其声明如下:
int raise (int sig);
重点: 值得注意的是,raise发出的信号,可被当前进程中同类型型号的signal函数捕获。
#include <chrono>
#include <iostream>
#include <csignal>
#include <thread>
using namespace std;
void signalHandler( int signum ) {
std::cout << "我来处理!" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
raise(SIGQUIT); // 3. 退出程序
}
int main() {
signal(SIGINT, signalHandler); // 1. signal函数,用于捕获 SIGINT 信号
int i=0;
while(++i){
std::cout << "keep run...." << i << std::endl;
if( i == 3 )
raise(SIGINT); // 2. raise, 发送SIGINT 信号
}
return 0;
}
可以看出当signal捕获信号后,main函数对应的程序暂停,不再打印。
本测试中signalHandler()发出 SIGQUIT信号,进程结束。
参考博客https://blog.csdn.net/u013271656/article/details/114537411