人们常用零和游戏的观点看世界,其实他们本不该这样。他们常常说:我比别人做的好,所以我就该发达。而在非零和游戏里,尽管你比别人做的好,你也可能和他一样潦倒。 —凯文·凯利《失控》
1. 概念
信号是事件发生时对进程的通知机制。有时可称之为软件中断。信号与硬件中断的相似之处在于打断了程序执行的正常流程。针对每个信号,都定义了一个唯一的(小)整数,从1开始顺序展开。信号分为两大类:
- 一组用于内核向进程通知事件,构成传统或者标准信号。Linux标准信号的编号范围为1~31。
另一组由实时信号构成。
信号到达后,进程视具体信号可以执行以下操作:
忽略信号,也就是说内核把信号丢弃,信号对进程没有产生任何影响(进程甚至不知道产生了信号)。
- 终止(杀死)进程。
- 产生核心转储文件,同时终止进程,所谓的核心转储文件就是内存映像文件。
- 停止进程,暂停进程的执行。
- 重启之前暂停的进程。
2. 信号类型
以下信号列表为不同硬件架构下对信号的编号;信号编号在架构上的差异在括号中予以说明:SPARC64(S)、HP/Compaq/DIgital Alpha(A)、MIPS(M)和HP PA-RISC(R)
名称 | 信号值 | 描述 |
---|---|---|
SIGABRT | 6 | 终止进程,当进程调用abort()函数时,系统向进程发送该信号。 |
SIGALRM | 14 | 实时定时器过期,经调用alarm()或setitimer()而设置的实时定时器一旦到期,内核将产生该信号。 |
SIGBUS | 7(SAMP=10) | 内错误存访问,产生该信号(总线错误,bus error)即表示发生了某种内存访问错误。 |
SIGCHLD | 17(SA=20,MP=18) | 终止或者停止子进程,当父进程的子进程终止(或因为调用了exit(),或者因为被信号杀死)时,(内核)将向父进程发送该信号。当父进程的某一子进程因收到信号而停止或恢复时,也可能会向父进程发送该信号。 |
SIGCONT | 18(SA=19,M=25,P=26) | 若进程状态为停止状态,接受信号后则继续运行;将该信号发送给已停止的进程,进程将会恢复运行(即在之后的某个时间点重新获得调度)。当接受信号的进程当前不处于停止状态时,默认情况下将忽略该信号。 |
SIGEMT | SAMP=7 | 硬件错误 |
SIGFPE | 8 | 算术异常;该信号因特定的算术错误而产生,比如除以0 |
SIGHUP | 1 | 挂起进程 |
SIGILL | 4 | 非法指令,如果进程试图执行非法(即格式不正确)的机器语言指令,系统将向进程发送该信号。 |
SIGINT | 2 | 终端中断,当用户输入终端中断字符(通常为Control-C),终端驱动程序将发送该信号给前台进程组。 |
SIGIO | 29(SA=23,MP=22) | I/O时可能产生,利用fcntl()系统调用,即特定类型(终端和套接字)的打开文件描述符发送的I/O事件产生该信号。 |
SIGKILL | 9 | 必杀进程,处理器程序无法将其阻塞、忽略或者捕获,故而“一击必杀” |
SIGPIPE | 13 | 管道断开,当某一进程试图向管道、FIFO或套接字写入信息时,如果这些设备并无相应的阅读进程,那么系统将产生该信号。 |
SIGPROF | 27(M=29,P=21) | 性能分析定时器过期,由settimer()调用所设置的性能分析刚一过期,内核就将产生该信号。性能分析定时器用于记录进程所使用的CPU时间。 |
SIGQUIT | 3 | 终端退出,当用户在键盘上键入退出字符(通常为Control+\)时,该信号将发往前台进程组。默认情况下,该信号终止进程,并生成可用于调试的核心转储文件。 |
SIGSEGV | 11 | 无效的内存引用 |
SIGSTKFLT | 16(SAM=undef,P=36) | 协处理器栈错误 |
SIGSTOP | 19(SA=17,M=23,P=24) | 停止进程,这是一个必停信号,处理器程序无法将其阻塞、忽略或者捕获。 |
SIGSYS | 31(SAMP=12) | 无效的系统调用 |
SIGTERM | 15 | 终止进程,这是用来终止进程的标准信号,也就是kill和killall命令所发送的默认信号。 |
SIGTRAP | 5 | 跟踪/断开陷阱,该信号用来实现断点调试功能以及strace命令所执行的跟踪系统调用功能。 |
SIGTSTP | 20(SA=18,M=24,p=25) | 终端停止,当用户在键盘上输入挂起字符(通常是Control+Z)时,将发送该信号给前台进程组,使其停止运行。 |
SIGTTIN | 21(M=26,P=27) | BG从终端读取,在作业控制shell下运行时,若后台进程组试图对终端进行read()操作,终端驱动程序则将向该进程组发送此信号。该信号默认停止进程。 |
SIGTTOU | 22(M=27,P=28) | BG从终端写,与SIGTIN信号类似,该信号用于输出。 |
SIGURG | 23(SA=16,M=21,P=29) | 套接字上的紧急数据,系统发送该信号给一个进程,表示套接字上存在带外(也称作紧急)数据。 |
SIGUSR1 | 10(SA=30,MP=16) | 用户自定义信号1,该信号与SIGUSR2信号供程序员自定义使用。内核绝不会为进程产生这些信号。进程可以使用这些信号来相互通知事件的发生,或是彼此同步。 |
SIGUSR2 | 12(SA=31,MP=17) | 用户自定义信号2,与SIGUSR1等同。 |
SIGVTALRM | 26(M=28,P=20) | 虚拟定时器过期,调用setitimer()设置的虚拟定时器刚一到期,内核就会产生该信号。虚拟定时器记录的是进程在用户态使用的CPU时间。 |
SIGWINCH | 28(M=20,P=23) | 终端窗口尺寸发生变化 |
SIGXCPU | 24(M=30,P=33) | 突破对CPU时间的限制,当进程的CPU时间超过对应的资源限制时,将发送此信号给进程。 |
SIGXFSZ | 25(M=31,P=34) | 突破对文件大小的限制,当进程试图增大文件而突破对进程文件大小的资源限制时,name将发送此信号给进程。 |
3. 信号处理器
- 信号处理程序(也称为信号捕捉器)是当指定信号传递给进程视将会调用的一个函数。
- 调用信号处理程序,可能会随时打断主程序流程;内核代表进程来调用处理器程序,当处理器返回时,主程序会在处理器打断的位置恢复执行。流程图如下:
4. 信号掩码
内核会为每个进程维护一个信号掩码,即一组信号,并将阻塞其针对该进程的传递。如果将遭阻塞的信号发送给某进程,那么对该进程的传递将延后,直至从进程信号掩码中移除该信号,从而解除阻塞为止。
5. 代码示例
#include<stdio.h>
#include<signal.h>
static void handleSignal(int sig)
{
printf("a ha\n");
}
int main(int argc, char *argv[])
{
int i = 0;
//处理控制台输入control+C命令;原来该信号直接回停止程序;现在改为输出a ha 语句。
if(signal(SIGINT,handleSignal) == SIG_ERR)
{
printf("signal handle fail!\n");
exit(0);
}
for(i = 0 ; ; i++)
{
printf("i=%d\n",i);
sleep(3);
}
}