Linux环境:C编程之信号机制
信号概述
什么是信号
信号是进程在运行过程中,由自身产生或由进程外部发过来的消息,用整型常量表示,在头文件<signal.h>
中定义了一系列宏表示不同的信号。
信号的来源
信号由内核产生,内核产生信号的情形有三种:
- 用户:用户能够通过输入 CTRL+c、Ctrl+\,或者是终端驱动程序分配给信号控制字符的其他任何键来请求内核产生信号;
- 内核:当进程执行出错时,内核会给进程发送一个信号,例如非法段存取(内存访问违规)、浮点数溢出等;
- 进程:一个进程可以通过系统调用 kill 给另一个进程发送信号,一个进程可以通过信号和另外一个进程进行通信
进程对信号的处理
进程对信号的处理方法有三种:
- 接收默认处理:进程如果不对该信号做特殊的处理,系统将采用默认的方式处理该信号,即终止进程的执行;
- 忽略信号:进程可以通过代码,显示地忽略某个信号的处理
- 捕捉信号并处理:进程可以事先注册信号处理函数,当接收到信号时,由信号处理函数自动捕捉并且处理
注意: 有两个信号既不能被忽略也不能被捕捉,它们是SIGKILL
和SIGSTOP
。即进程接收到这两个信号后,只能接受系统的默认处理,即终止进程。SIGSTOP
是暂停进程。SIGKILL
是杀死进程。
常用信号
常用的信号如下:
信号 | 值 | 默认处理 | 说明 |
---|---|---|---|
SIGHUP | 1 | A | 在控制终端上是挂起信号, 或者控制进程结束 |
SIGINT | 2 | A | 从键盘输入的中断 |
SIGQUIT | 3 | C | 从键盘输入的退出 |
SIGILL | 4 | C | 无效硬件指令 |
SIGABRT | 6 | C | 非正常终止 |
SIGFPE | 8 | C | 浮点运算例外 |
SIGKILL | 9 | AEF | 杀死进程信号 |
SIGSEGV | 11 | C | 无效的内存引用 |
SIGPIPE | 13 | A | 管道中止: 写入无人读取的管道 |
SIGALRM | 14 | A | 来自 alarm(2) 的超时信号 |
SIGTERM | 15 | A | 终止信号 |
SIGUSR1 | 30,10,16 | A | 用户定义的信号 1 |
SIGUSR2 | 31,12,17 | A | 用户定义的信号 2 |
SIGCHLD | 20,17,18 | B | 子进程结束或停止 |
SIGCONT | 19,18,25 | 继续停止的进程 | |
SIGSTOP | 17,19,23 | DEF | 停止进程 |
SIGTSTP | 18,20,24 | D | 终端上发出的停止信号 |
SIGTTIN | 21,21,26 | D | 后台进程试图从控制终端(输入 |
SIGTTOU | 22,22,27 | D | 后台进程试图在控制终端输出 |
默认处理字母代表的含义:
A 缺省动作是结束进程.
B 缺省动作是忽略这个信号.
C 缺省动作是结束进程, 并且核心转储.
D 缺省动作是停止进程.
E 信号不能被捕获.
F 信号不能被忽略.
signal 信号处理机制
signal函数
signal
函数原型:
sighandler_t signal(int signum, sighandler_t handler);
handler
是信号处理函数,负责处理信号。sighandler_t
为自定义类型,是一个函数指针,指向一个返回值为void,参数为一个int的函数。通过该类型可以定义信号处理函数,对传入的信号进行处理。可以是系统默认的信号处理函数SIG_DFL
(表示交由系统缺省处理,相当于白注册了)或SIG_IGN
(表示忽略掉该信号而不做任何处理)signum
代表某种类型的信号。signal
函数注册成功返回该信号以前处理函数的地址,失败返回SIG_ERR
,即-1的强制类型转换。- 函数原理:通过
signal
函数可以将特定类型的信号与一个信号处理函数联系起来,称为信号处理函数的注册。注册之后进程收到的该类型的信号将全部交给对应的信号处理函数处理。一个信号处理函数可以对多种信号进行处理,只需要用signal多次注册就可以了。从而实现了对信号的捕捉处理。
示例程序:信号捕捉和打断
void sighandle1(int sig)
{
printf("信号处理%d:打印0-9\n",sig);
for(int i=0;i<10;i++)
{
printf("%d\n",i);
sleep(1);
}
}
void sighandle2(int sig)
{
printf("信号处理%d:\n中断\n",sig);
int i = 0;
while(i++<10)
{
sleep(1);
printf