什么是信号
软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。在软件层次上是对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息。
收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:
Ø 类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。
Ø 忽略某个信号(SIG_IGN),对该信号不做任何处理,就象未发生过一样。
Ø 对该信号的处理保留系统的默认值(SIG_DFL),这种缺省操作,对大部分的信号的缺省操作是使得进程终止。进程通过系统调用signal来指定进程对某个信号的处理行为。
查看目前支持的信号值
[root@bogon demo]# kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
信号的分类
可以从两个不同的分类角度对信号进行分类:
Ø 可靠性方面:可靠信号与不可靠信号;
Ø 与时间的关系上:实时信号与非实时信号。
可靠信号与不可靠信号
Linux信号机制基本上是从Unix系统中继承过来的。早期Unix系统中的信号机制比较简单和原始,信号值小于SIGRTMIN的信号都是不可靠信号。这就是"不可靠信号"的来源。它的主要问题是信号可能丢失。
随着时间的发展,实践证明了有必要对信号的原始机制加以改进和扩充。由于原来定义的信号已有许多应用,不好再做改动,最终只好又新增加了一些信号,并在一开始就把它们定义为可靠信号,这些信号支持排队,不会丢失。
信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。Linux在支持新版本的信号安装函数sigation()以及信号发送函数sigqueue()的同时,仍然支持早期的signal()信号安装函数,支持信号发送函数kill()。
信号的可靠与不可靠只与信号值有关,与信号的发送及安装函数无关。目前linux中的signal()是通过sigation()函数实现的,因此,即使通过signal()安装的信号,在信号处理函数的结尾也不必再调用一次信号安装函数。同时,由signal()安装的实时信号支持排队,同样不会丢失。
对于目前linux的两个信号安装函数:signal()及sigaction()来说,它们都不能把SIGRTMIN以前的信号变成可靠信号(都不支持排队,仍有可能丢失,仍然是不可靠信号),而且对SIGRTMIN以后的信号都支持排队。这两个函数的最大区别在于,经过sigaction安装的信号都能传递信息给信号处理函数,而经过signal安装的信号不能向信号处理函数传递信息。对于信号发送函数来说也是一样的。
实时信号与非实时信号
早期Unix系统只定义了32种信号,前32种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作。如按键盘的CTRL ^C时,会产生SIGINT信号,对该信号的默认反应就是进程终止。后32个信号表示实时信号,等同于前面阐述的可靠信号。这保证了发送的多个实时信号都被接收。
非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。
信号相关函数说明
#include <signal.h>
void (*signal(int signum, void (*handler))(int)))(int);
如果该函数原型不容易理解的话,可以参考下面的分解方式来理解:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler));
第一个参数指定信号的值,第二个参数指定针对前面信号值的处理,可以忽略该信号(参数设为SIG_IGN);可以采用系统默认方式处理信号(参数设为SIG_DFL);也可以自己实现处理方式(参数指定一个函数地址)。
如果signal()调用成功,返回最后一次为安装信号signum而调用signal()时的handler值;失败则返回SIG_ERR。
传递给信号处理例程的整数参数是信号值,这样可以使得一个信号处理例程处理多个信号。
信号案例说明
案例设计
Ø 进程收到SIGHUP,SIGINT,SIGQUIT,提示收到对应的信号,不做任何处理
Ø 进程中有个计数器,当用户发出SIGUSR1,SIGUSR2时,打印出这个计数器的值
Ø 对信号9(SIGKILL)和信号11(SIGSEGV)分别做忽略和默认处理
代码说明:
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <limits.h> #include <string.h> #include <signal.h> int count = 1; void deal_sig(int signum) { switch(signum) { case 1: // SIGHUP printf("signal SIGHUP cached "); break; case 2: // SIGINT printf("signal SIGINT cached "); break; case 3: // SIGQUIT printf("signal SIGQUIT cached "); break; case 10: // SIGUSR1 printf("signal SIGUSR1 cached, count = %d ", count); break; case 12: // SIGUSR2 printf("signal SIGUSR2 cached, count = %d ", count); break; } return; } int main(int argc, char** argv) { signal(SIGHUP, deal_sig); signal(SIGINT, deal_sig); signal(SIGQUIT, deal_sig); signal(SIGUSR1, deal_sig); signal(SIGUSR2, deal_sig); signal(SIGKILL, SIG_IGN); signal(SIGSEGV, SIG_DFL); while(1) { count++; sleep(1); } return 0; }
结果说明:
tty1:
[root@bogon ipc]# g++ -g -o ipc_signal ipc_signal.cpp
[root@bogon ipc]# ./ipc_signal
^Csignal SIGINT cached // 这里按下 CTRL + C
signal SIGHUP cached
signal SIGINT cached
signal SIGQUIT cached
signal SIGUSR1 cached, count = 73
signal SIGUSR2 cached, count = 79
Killed // 这里发现SIGKILL信号是忽略不掉的,进程还是退出了
[root@bogon ipc]# ./ipc_signal
Segmentation fault (core dumped) // 收到段错误信号,产生core并退出
tty2:
[root@bogon ipc]# ps axu|grep ipc_signal
root 20380 0.0 0.0 11740 828 pts/1 S+ 00:50 0:00 ./ipc_signal
root 20389 0.0 0.0 103252 812 pts/2 S+ 00:50 0:00 grep ipc_signal
[root@bogon ipc]# kill -HUP 20380
[root@bogon ipc]# kill -INT 20380
[root@bogon ipc]# kill -QUIT 20380
[root@bogon ipc]# kill -USR1 20380
[root@bogon ipc]# kill -USR2 20380
[root@bogon ipc]# kill -KILL 20380
[root@bogon ipc]# ps axu|grep ipc_signal
root 20445 0.0 0.0 11736 780 pts/1 S+ 00:52 0:00 ./ipc_signal
root 20448 0.0 0.0 103252 812 pts/2 S+ 00:53 0:00 grep ipc_signal
[root@bogon ipc]# kill -SEGV 20445