- 信号机制是进程之间相互传递消息的一种方法,信号全称为软中断信号,也有人称软中断。从它的命名可以看出,它的实质和使用很像中断,所有,信号可以说是进程控制的一部分。
- 信号
signal
处理是Linux
程序的一个特色,用信号处理来模拟操作系统的中断功能。#include <csignal>
或#include <signal.h>
是处理信号的C-library
。
1. 什么是信号
#include <csignal>
或#include <signal.h>
是处理信号的C-library
。该库包含signal
与raise
两个功能函数。
I. 函数signal
用于捕获信号,可指定信号处理的方式。
II. 函数raise
产生一个信号,并向当前正在执行的程序发送该信号。
- 信号
signal
可以理解为由操作系统传给程序(进程)的事件,只是用来通知程序发生了什么事件,并不会传递给该进程任何数据. - 信号是一种中断,因为它可以改变程序的流程。当信号传递给进程时,进程将停下其正在执行的操作,并去处理或忽略该信号
异步事件
。
异步事件
- 查看信号的方式是一种处理异步
事件的机制。
▶ 当程序
通过 signal 函数捕获信号后,若signal
函数第二参数为函数指针,则调用signal 函数的程序会阻塞(暂停在signal
函数这句),直至异步线程(进入函数指针)return
。程序将从暂停点恢复执行。
▶ 此外,进程之间可以互相通过系统调用kill
发送软中断信号
。
C++ signum.h
中通过 #define 定义了以下信号
2. 函数 signal
- 函数
signal
用于捕获信号,可指定信号处理的方式,函数声明如下:
void (*signal(int sig, void (*func)(int)))(int);
第一个参数
sig
指明了所要处理的信号类型,它可以取除了SIGKILL
和SIGSTOP
外的任何一种信号。
第二个参数描述了与信号关联的动作,下面将会对这三个参数进行讲解。
- 当程序收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:
I. 默认处理:对信号进行该信号的系统默认处理,第二参数为SIG_DFL
。
II. 忽略信号:忽略该信号,第二参数为SIG_IGN
。
III. 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;
}
hello world!
hello world!
hello world!
^C
- 该程序包含一个死循环,循环内以一秒为间隔 打印语句。
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;
}
hello world!
hello world!
^Chello world!
CChello world!
^Chello world!
hello world!
^\退出 (核心已转储)
- 该程序包含一个死循环,循环内以一秒为间隔 打印语句。
SIGINT
信号代表由InterruptKey
产生,通常是CTRL +C
。执行上述代码时,按下CTRL + C
后程序没有反应。- 如果我们想结束该程序可以按下
CTRL +\
,CTRL +\
组合键会产生SIGQUIT
信号,此信号并没有被忽略。
2.3 自定义函数
void (*signal(int sig, void (*func)(int)))(int);
- 第二参数为
函数指针
:当signal
函数捕获信号后,通过指定函数进行处理。 - PS: 此函数必须在
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;
}
^C我来处理!
程序结束.
- 该程序声明了信号类型为
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;
}
keep run…1
keep run…2
keep run…3
我来处理!
退出 (核心已转储)
- 可以看出当
signal
捕获信号后,main函数
对应的程序暂停,不再打印。 - 本测试中
signalHandler()
发出SIGQUIT
信号,进程结束。
4. 其他信号
-
linux
系统下,运行命令kill -l
可以看到Linux支持的信号列表。
-
一些常用的
Signal
如下:Signal Description SIGALRM 用alarm函数设置的timer超时或setitimer函数设置的interval timer超时 SIGBUS 某种特定的硬件异常,通常由内存访问引起 SIGCANCEL 由Solaris Thread Library内部使用,通常不会使用 SIGCHLD 进程Terminate或Stop的时候,SIGCHLD会发送给它的父进程。缺省情况下该Signal会被忽略 SIGCONT 当被stop的进程恢复运行的时候,自动发送 SIGEMT 和实现相关的硬件异常 SIGFPE 数学相关的异常,如被0除,浮点溢出,等等 SIGFREEZE Solaris专用,Hiberate或者Suspended时候发送 SIGHUP 发送给具有Terminal的Controlling Process,当terminal 被disconnect时候发送 SIGILL 非法指令异常 SIGINFO BSD signal。由Status Key产生,通常是CTRL+T。发送给所有Foreground Group的进程 SIGINT 由Interrupt Key产生,通常是CTRL+C或者DELETE。发送给所有ForeGround Group的进程 SIGIO 异步IO事件 SIGIOT 实现相关的硬件异常,一般对应SIGABRT SIGKILL 无法处理和忽略。中止某个进程 SIGLWP 由Solaris Thread Libray内部使用 SIGPIPE 在reader中止之后写Pipe的时候发送 SIGPOLL 当某个事件发送给Pollable Device的时候发送 SIGPROF Setitimer指定的Profiling Interval Timer所产生 SIGPWR 和系统相关。和UPS相关。 SIGQUIT 输入Quit Key的时候(CTRL+\)发送给所有Foreground Group的进程 SIGSEGV 非法内存访问 SIGSTKFLT Linux专用,数学协处理器的栈异常 SIGSTOP 中止进程。无法处理和忽略。 SIGSYS 非法系统调用 SIGTERM 请求中止进程,kill命令缺省发送 SIGTHAW Solaris专用,从Suspend恢复时候发送 SIGTRAP 实现相关的硬件异常。一般是调试异常 SIGTSTP Suspend Key,一般是Ctrl+Z。发送给所有Foreground Group的进程 SIGTTIN 当Background Group的进程尝试读取Terminal的时候发送 SIGTTOU 当Background Group的进程尝试写Terminal的时候发送 SIGURG 当out-of-band data接收的时候可能发送 SIGUSR1 用户自定义signal 1 SIGUSR2 用户自定义signal 2 SIGVTALRM setitimer函数设置的Virtual Interval Timer超时的时候 SIGWAITING Solaris Thread Library内部实现专用 SIGWINCH 当Terminal的窗口大小改变的时候,发送给Foreground Group的所有进程 SIGXCPU 当CPU时间限制超时的时候 SIGXFSZ 进程超过文件大小限制 SIGXRES Solaris专用,进程超过资源限制的时候发