1. 基本概念
1)中断
中止(注意不是终止)当前正在执行的程序,转而执行其它任务。
硬件中断:来自硬件设备的中断。
软件中断:来自其它程序的中断。
2)信号是一种软件中断
信号提供了一种以异步方式执行任务的机制。
3)常见信号
SIGHUP(1):连接断开信号
如果终端接口检测一个连接断开,
则将此信号发送给与该终端相关的控制进程(会话首进程)。
默认动作:终止。
SIGINT(2):终端中断符信号
用户按中断键(Ctrl+C),产生此信号,
并送至前台进程组的所有进程。
默认动作:终止。
(相当于:kill -2 20505)
SIGQUIT(3):终端退出符信号
用户按退出键(Ctrl+\),产生此信号,
并送至前台进程组的所有进程。
默认动作:终止+core。
(core 相当于一个快照,用于时候诊断)
SIGILL(4):非法硬件指令信号
进程执行了一条非法硬件指令。
默认动作:终止 + core。
SIGTRAP(5):硬件故障信号
指示一个实现定义的硬件故障。常用于调试。
默认动作:终止 + core。
SIGABRT(6):异常终止信号
调用abort函数,产生此信号。
默认动作:终止 + core。
SIGBUS(7):总线错误信号
指示一个实现定义的硬件故障。常用于内存故障。
默认动作:终止 + core。
SIGFPE(8):算术异常信号
表示一个算术运算异常,例如除以 0、浮点溢出等。
默认动作:终止 + core。
SIGKILL(9):终止信号
不能被捕获或忽略,常用于杀死进程。
默认动作:终止。
SIGUSR1(10):用户定义信号
用户定义信号,用于应用程序。
默认动作:终止。
SIGSEGV(11):段错误信号
试图访问未分配的内存,或向没有写权限的内存写入数据。
默认动作:终止 + core。
SIGUSR2(12):用户定义信号
用户定义信号,用于应用程序。
默认动作:终止。
SIGPIPE(13):管道异常信号
写管道时读进程已终止,
或写 SOCK_STREAM 类型套接字时连接已断开,均产生此信号。
默认动作:终止。
SIGALRM(14):闹钟信号
以 alarm 函数设置的计时器到期,
或以 setitimer 函数设置的间隔时间到期,均产生此信号。
默认动作:终止。
SIGTERM(15):终止信号
由 kill 命令发送的系统默认终止信号。
默认动作:终止。
(和 9 的区别是:15 可以被忽略)
SIGSTKFLT(16):数协器栈故障信号
表示数学协处理器发生栈故障。
默认动作:终止。
SIGCHLD(17):子进程状态改变信号
在一个进程终止或停止时,将此信号发送给其父进程。
默认动作:忽略。
SIGCONT(18):使停止的进程继续
向处于停止状态的进程发送此信号,令其继续运行。
默认动作:继续/忽略。
SIGSTOP(19):停止信号
不能被捕获或忽略。停止一个进程。
默认动作:停止进程。
SIGTSTP(20):终端停止符信号。
用户按停止键(Ctrl+Z),产生此信号,
并送至前台进程组的所有进程。
默认动作:停止进程。
SIGTTIN(21):后台读控制终端信号
后台进程组中的进程试图读其控制终端,产生此信号。
默认动作:停止。
SIGTTOU(22):后台写控制终端信号
后台进程组中的进程试图写其控制终端,产生此信号。
默认动作:停止。
SIGURG(23):紧急情况信号
有紧急情况发生,或从网络上接收到带外数据,产生此信号。
默认动作:忽略。
SIGXCPU(24):超过 CPU 限制信号
进程超过了其软 CPU 时间限制,产生此信号。
默认动作:终止 + core。
SIGXFSZ(25):超过文件长度限制信号
进程超过了其软文件长度限制,产生此信号。
默认动作:终止 + core。
SIGVTALRM(26):虚拟闹钟信号
以 setitimer 函数设置的虚拟间隔时间到期,产生此信号。
默认动作:终止。
SIGPROF(27):虚拟梗概闹钟信号
以 setitimer 函数设置的虚拟梗概统计间隔时间到期,产生此信号。
默认动作:终止。
SIGWINCH(28):终端窗口大小改变信号
以 ioctl 函数更改窗口大小,产生此信号。
默认动作:忽略。
SIGIO(29):异步 I/O 信号
指示一个异步 I/O 事件。
默认动作:终止。
SIGPWR(30):电源失效信号
电源失效,产生此信号。
默认动作:终止。
SIGSYS(31):非法系统调用异常。
指示一个无效的系统调用。
默认动作:终止 + core。
4)不可靠信号(非实时信号)
a. 那些建立在早期机制上的信号被称为“不可靠信号”。
小于 SIGRTMIN(34) 的信号都是不可靠信号。
b. 不支持排队,可能会丢失。同一个信号产生多次,进程可能只收到一次该信号。
c. 进程每次处理完这些信号后,对相应信号的响应被自动恢复为默认动作,
除非显示地通过 signal 函数重新设置一次信号处理程序。
5)可靠信号(实时信号)
a. 位于 [SIGRTMIN(34), SIGRTMAX(64)] 区间的信号都是可靠信号。
b. 支持排队,不会丢失。
c. 无论可靠信号还是不可靠信号,都可以通过 sigqueue/sigaction 函数发送/安装,
以获得比其早期版本 kill/signal 函数更可靠的使用效果。
6)信号的来源
a. 硬件异常:除 0、无效内存访问等。
这些异常通常被硬件(驱动)检测到,并通知系统内核。
系统内核再向引发这些异常的进程递送相应的信号。
b. 软件异常:
通过 kill/raise/alarm/setitimer/sigqueue 函数产生的信号。
7)信号处理
a. 忽略。
b. 终止进程。
c. 终止进程同时产生 core 文件。
d. 捕获并处理。
当信号发生时,内核会调用一个事先注册好的用户函数(信号处理函数)。
范例:loop.c
#include <stdio.h>
int main (void) {
printf ("%u进程:我在运行,按<Ctrl+C>或<Ctrl+\\>终止...\n",
getpid ());
for (;;); // 死循环,用于手动终止
return 0;
}
运行测试:
# a.out
按中断键(Ctrl+C),发送 SIGINT(2) 终端中断符信号。
# a.out
按退出键(Ctrl+\),发送 SIGQUIT(3) 终端退出符信号。
2. signal
#include <signal.h>
typedef void (*sighandler_t) (int);
sighandler_t signal (int signum,
sighandler_t handler);
signum - 信号码,也可使用系统预定义的常量宏,如 SIGINT 等。
handler - 信号处理函数指针或以下常量:
SIG_IGN: 忽略该信号;
SIG_DFL: 默认处理。
成功返回原来的信号处理函数指针或 SIG_IGN/SIG_DFL 常量,
失败返回 SIG_ERR。
1)在某些 Unix 系统上,通过 signal 函数注册的信号处理函数只一次有效,
即内核每次调用信号处理函数前,会将对该信号的处理自动恢复为默认方式。
为了获得持久有效的信号处理,可以在信号处理函数中再次调用 signal 函数,重新注册一次。
例如:
void sigint (int signum) {
. . .
signal (SIGINT, sigint);
}
int main (void) {
. . .
signal (SIGINT, sigint);
. . .
}
2)SIGKILL/SIGSTOP 信号不能被忽略,也不能被捕获。
3)普通用户只能给自己的进程发送信号,root用户可以给任何进程发送信号。
范例:signal.c(自定义信号处理方式)
#include <stdio.h>
#include <signal.h>
void sigint (int signum) {
printf ("%u进程:收到SIGINT信号!\n", getpid ());
// signal (SIGINT, SIG_DFL);
// signal (SIGINT, sigint);
}
void sigkill (int signum) {
printf ("%u进程:收到SIGKILL信号!\n", getpid ());
}
int main (void) {
/*
if (signal (SIGINT, SIG_IGN) == SIG_ERR) {
perror ("signal");
return -1;
}
*/
// 截获 <Ctrl+C>,自定义处理方式
// 在新版 linux 系统里,该信号的处理不会自动恢复为默认方式,也就是信号回调;
if (signal (SIGINT, sigint) == SIG_ERR) {
perror ("signal");
return -1;
}
/*
if (signal (SIGKILL, SIG_IGN) == SIG_ERR) { // 报错,SIGKILL 不能被忽略
perror ("signal");
return -1;
}
if (signal (SIGKILL, sigkill) == SIG_ERR) { // 报错,SIGKILL 不能被捕获
perror ("signal");
return -1;
}
*/
printf (&#
1)中断
中止(注意不是终止)当前正在执行的程序,转而执行其它任务。
硬件中断:来自硬件设备的中断。
软件中断:来自其它程序的中断。
2)信号是一种软件中断
信号提供了一种以异步方式执行任务的机制。
3)常见信号
SIGHUP(1):连接断开信号
如果终端接口检测一个连接断开,
则将此信号发送给与该终端相关的控制进程(会话首进程)。
默认动作:终止。
SIGINT(2):终端中断符信号
用户按中断键(Ctrl+C),产生此信号,
并送至前台进程组的所有进程。
默认动作:终止。
(相当于:kill -2 20505)
SIGQUIT(3):终端退出符信号
用户按退出键(Ctrl+\),产生此信号,
并送至前台进程组的所有进程。
默认动作:终止+core。
(core 相当于一个快照,用于时候诊断)
SIGILL(4):非法硬件指令信号
进程执行了一条非法硬件指令。
默认动作:终止 + core。
SIGTRAP(5):硬件故障信号
指示一个实现定义的硬件故障。常用于调试。
默认动作:终止 + core。
SIGABRT(6):异常终止信号
调用abort函数,产生此信号。
默认动作:终止 + core。
SIGBUS(7):总线错误信号
指示一个实现定义的硬件故障。常用于内存故障。
默认动作:终止 + core。
SIGFPE(8):算术异常信号
表示一个算术运算异常,例如除以 0、浮点溢出等。
默认动作:终止 + core。
SIGKILL(9):终止信号
不能被捕获或忽略,常用于杀死进程。
默认动作:终止。
SIGUSR1(10):用户定义信号
用户定义信号,用于应用程序。
默认动作:终止。
SIGSEGV(11):段错误信号
试图访问未分配的内存,或向没有写权限的内存写入数据。
默认动作:终止 + core。
SIGUSR2(12):用户定义信号
用户定义信号,用于应用程序。
默认动作:终止。
SIGPIPE(13):管道异常信号
写管道时读进程已终止,
或写 SOCK_STREAM 类型套接字时连接已断开,均产生此信号。
默认动作:终止。
SIGALRM(14):闹钟信号
以 alarm 函数设置的计时器到期,
或以 setitimer 函数设置的间隔时间到期,均产生此信号。
默认动作:终止。
SIGTERM(15):终止信号
由 kill 命令发送的系统默认终止信号。
默认动作:终止。
(和 9 的区别是:15 可以被忽略)
SIGSTKFLT(16):数协器栈故障信号
表示数学协处理器发生栈故障。
默认动作:终止。
SIGCHLD(17):子进程状态改变信号
在一个进程终止或停止时,将此信号发送给其父进程。
默认动作:忽略。
SIGCONT(18):使停止的进程继续
向处于停止状态的进程发送此信号,令其继续运行。
默认动作:继续/忽略。
SIGSTOP(19):停止信号
不能被捕获或忽略。停止一个进程。
默认动作:停止进程。
SIGTSTP(20):终端停止符信号。
用户按停止键(Ctrl+Z),产生此信号,
并送至前台进程组的所有进程。
默认动作:停止进程。
SIGTTIN(21):后台读控制终端信号
后台进程组中的进程试图读其控制终端,产生此信号。
默认动作:停止。
SIGTTOU(22):后台写控制终端信号
后台进程组中的进程试图写其控制终端,产生此信号。
默认动作:停止。
SIGURG(23):紧急情况信号
有紧急情况发生,或从网络上接收到带外数据,产生此信号。
默认动作:忽略。
SIGXCPU(24):超过 CPU 限制信号
进程超过了其软 CPU 时间限制,产生此信号。
默认动作:终止 + core。
SIGXFSZ(25):超过文件长度限制信号
进程超过了其软文件长度限制,产生此信号。
默认动作:终止 + core。
SIGVTALRM(26):虚拟闹钟信号
以 setitimer 函数设置的虚拟间隔时间到期,产生此信号。
默认动作:终止。
SIGPROF(27):虚拟梗概闹钟信号
以 setitimer 函数设置的虚拟梗概统计间隔时间到期,产生此信号。
默认动作:终止。
SIGWINCH(28):终端窗口大小改变信号
以 ioctl 函数更改窗口大小,产生此信号。
默认动作:忽略。
SIGIO(29):异步 I/O 信号
指示一个异步 I/O 事件。
默认动作:终止。
SIGPWR(30):电源失效信号
电源失效,产生此信号。
默认动作:终止。
SIGSYS(31):非法系统调用异常。
指示一个无效的系统调用。
默认动作:终止 + core。
4)不可靠信号(非实时信号)
a. 那些建立在早期机制上的信号被称为“不可靠信号”。
小于 SIGRTMIN(34) 的信号都是不可靠信号。
b. 不支持排队,可能会丢失。同一个信号产生多次,进程可能只收到一次该信号。
c. 进程每次处理完这些信号后,对相应信号的响应被自动恢复为默认动作,
除非显示地通过 signal 函数重新设置一次信号处理程序。
5)可靠信号(实时信号)
a. 位于 [SIGRTMIN(34), SIGRTMAX(64)] 区间的信号都是可靠信号。
b. 支持排队,不会丢失。
c. 无论可靠信号还是不可靠信号,都可以通过 sigqueue/sigaction 函数发送/安装,
以获得比其早期版本 kill/signal 函数更可靠的使用效果。
6)信号的来源
a. 硬件异常:除 0、无效内存访问等。
这些异常通常被硬件(驱动)检测到,并通知系统内核。
系统内核再向引发这些异常的进程递送相应的信号。
b. 软件异常:
通过 kill/raise/alarm/setitimer/sigqueue 函数产生的信号。
7)信号处理
a. 忽略。
b. 终止进程。
c. 终止进程同时产生 core 文件。
d. 捕获并处理。
当信号发生时,内核会调用一个事先注册好的用户函数(信号处理函数)。
范例:loop.c
#include <stdio.h>
int main (void) {
printf ("%u进程:我在运行,按<Ctrl+C>或<Ctrl+\\>终止...\n",
getpid ());
for (;;); // 死循环,用于手动终止
return 0;
}
运行测试:
# a.out
按中断键(Ctrl+C),发送 SIGINT(2) 终端中断符信号。
# a.out
按退出键(Ctrl+\),发送 SIGQUIT(3) 终端退出符信号。
2. signal
#include <signal.h>
typedef void (*sighandler_t) (int);
sighandler_t signal (int signum,
sighandler_t handler);
signum - 信号码,也可使用系统预定义的常量宏,如 SIGINT 等。
handler - 信号处理函数指针或以下常量:
SIG_IGN: 忽略该信号;
SIG_DFL: 默认处理。
成功返回原来的信号处理函数指针或 SIG_IGN/SIG_DFL 常量,
失败返回 SIG_ERR。
1)在某些 Unix 系统上,通过 signal 函数注册的信号处理函数只一次有效,
即内核每次调用信号处理函数前,会将对该信号的处理自动恢复为默认方式。
为了获得持久有效的信号处理,可以在信号处理函数中再次调用 signal 函数,重新注册一次。
例如:
void sigint (int signum) {
. . .
signal (SIGINT, sigint);
}
int main (void) {
. . .
signal (SIGINT, sigint);
. . .
}
2)SIGKILL/SIGSTOP 信号不能被忽略,也不能被捕获。
3)普通用户只能给自己的进程发送信号,root用户可以给任何进程发送信号。
范例:signal.c(自定义信号处理方式)
#include <stdio.h>
#include <signal.h>
void sigint (int signum) {
printf ("%u进程:收到SIGINT信号!\n", getpid ());
// signal (SIGINT, SIG_DFL);
// signal (SIGINT, sigint);
}
void sigkill (int signum) {
printf ("%u进程:收到SIGKILL信号!\n", getpid ());
}
int main (void) {
/*
if (signal (SIGINT, SIG_IGN) == SIG_ERR) {
perror ("signal");
return -1;
}
*/
// 截获 <Ctrl+C>,自定义处理方式
// 在新版 linux 系统里,该信号的处理不会自动恢复为默认方式,也就是信号回调;
if (signal (SIGINT, sigint) == SIG_ERR) {
perror ("signal");
return -1;
}
/*
if (signal (SIGKILL, SIG_IGN) == SIG_ERR) { // 报错,SIGKILL 不能被忽略
perror ("signal");
return -1;
}
if (signal (SIGKILL, sigkill) == SIG_ERR) { // 报错,SIGKILL 不能被捕获
perror ("signal");
return -1;
}
*/
printf (&#