信号(Signals)是Unix、类Unix以及其余POSIX兼容的操做系统中进程间通信的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操做系统中断了进程正常的控制流程,此时,任何非原子操做都将被中断。若是进程定义了信号的处理函数,那么它将被执行,不然就执行默认的处理函数。html
信号是进程间通讯机制中惟一的异步通讯机制,能够看做是异步通知,通知接收信号的进程有哪些事情发生了。也能够简单理解为信号是某种形式上的软中断。linux
通常状况下,信号的来源可分为如下三种:git
kill
命令向进程发送任务信号、进程调用kill
或sigqueue
函数发送信号、当检测到某种软件条件已经具有时发出信号,如由alarm
或settimer
设置的定时器超时时将生成SIGALRM
信号等多种情景都可产生信号。Ctrl+C
将产生一个SIGINT
信号,Ctrl+\
产生一个SIGQUIT
信号等。可运行kill -l
查看Linux支持的信号列表:github
sl@Li:~/Works$ 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中系统一共支持64种信号,其中1到31号信号为普通讯号(也程为不可靠信号),34到64为实时信号(可靠信号)。shell
可靠信号与不可靠信号的区别:微信
如下列出几个经常使用的信号:异步
信号 | 描述 |
---|---|
SIGHUP | 当用户退出终端时,由该终端开启的全部进程都退接收到这个信号,默认动做为终止进程。 |
SIGINT | 程序终止(interrupt)信号, 在用户键入INTR字符(一般是Ctrl+C )时发出,用于通知前台进程组终止进程。 |
SIGQUIT | 和SIGINT 相似, 但由QUIT字符(一般是Ctrl+\ )来控制. 进程在因收到SIGQUIT 退出时会产生core 文件, 在这个意义上相似于一个程序错误信号。 |
SIGKILL | 用来当即结束程序的运行. 本信号不能被阻塞、处理和忽略。 |
SIGTERM | 程序结束(terminate)信号, 与SIGKILL 不一样的是该信号能够被阻塞和处理。一般用来要求程序本身正常退出。 |
SIGSTOP | 中止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略. |
要注意信号处理函数的可重入问题,由于信号处理函数执行过程当中有可能被其余信号再次中断,这时程序会跳到另外一个信号的处理函数中,处理完成后再次返回当前处理函数,编写本身定义的信号处理函数的时候必定要注意这一点。信号能够理解为“软中断”,这样可重入函数就很好理解了。函数
内核处理一个进程收到的信号的时机是在一个进程从内核态返回用户态时。 因此,当一个进程在内核态下运行时,软中断信号并不当即起做用,要等到将返回用户态时才处理。进程只有处理完信号才会返回用户态,进程在用户态下不会有未处理完的信号。ui
内核处理一个进程收到的软中断信号是在该进程的上下文中,所以,进程必须处于运行状态。当进程接收到一个它忽略的信号时,进程丢弃该信号,就像没有收到该信号似的继续运行。spa
若是进程收到一个要捕捉的信号,那么进程从内核态返回用户态时执行用户定义的函数。并且执行用户定义的函数的方法很巧妙,内核在用户栈上建立一个新的层,该层中将返回地址的值设置成用户定义的处理函数的地址,这样进程从内核返回弹出栈顶时就返回到用户定义的函数处,从函数返回再弹出栈顶时,才返回原先进入内核的地方。 这样作的缘由是用户定义的处理函数不能且不容许在内核态下执行(若是用户定义的函数在内核态下运行的话,用户就能够得到任何权限)。
下面引用一个例子说明这个过程:
SIGQUIT
信号的处理函数sighandler
。main
函数,这时发生中断或异常切换到内核态。main
函数以前检查到有信号SIGQUIT
递达。main
函数的上下文继续执行,而是执行sighandler
函数,sighandler
和main
函数使用不一样的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程。sighandler
函数返回后自动执行特殊的系统调用sigreturn
再次进入内核态。main
函数的上下文继续执行了。下面的代码收到程序退出信号后会执行用户定义的信号处理函数来替代系统默认的处理程序。
#include<stdlib.h>
#include<stdio.h>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>
void sig_handle(int sig) {
printf("received signal: %d, quit.\n", sig);
exit(0);
}
int main () {
signal(SIGINT, sig_handle);
signal(SIGKILL, sig_handle);
signal(SIGSEGV, sig_handle);
signal(SIGTERM, sig_handle);
int i = 0;
while (1) {
printf("%d\n", ++i);
sleep(2);
}
printf("main quit.");
return 0;
}
复制代码
运行结果:
1
2
received signal: 15, quit.
复制代码
这段代码功能与上面的例子差很少,信号也能够传参,可是更多的是通知功能,可传递的信息很是有限,若是须要传递大量的信息,能够考虑其余进程间通讯方式。
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
void new_op(int, siginfo_t*, void*);
int main() {
if (NULL == freopen("sigproc.log", "w", stdout)) {
fprintf(stderr, "error redirecting stdout\n");
}
struct sigaction act;
sigemptyset(&act.sa_mask); //sa_mask指定在信号处理程序执行过程当中,哪些信号应当被阻塞。缺省状况下当前信号自己被阻塞,防止信号的嵌套发送
sigaddset(&act.sa_mask, SIGTERM);
sigaddset(&act.sa_mask, SIGINT);
act.sa_flags = SA_SIGINFO; //SA_SIGINFO,当设定了该标志位时,表示信号附带的参数能够被传递到信号处理函数中
act.sa_sigaction = new_op;
if (sigaction(SIGINT, &act, NULL) < 0) {
printf("install sigal error\n");
}
if (sigaction(SIGTERM, &act, NULL) < 0) {
printf("install sigal error\n");
}
if (sigaction(SIGHUP, &act, NULL) < 0) {
printf("install sigal error\n");
}
int i = 0;
while (1) {
printf("%d\n", ++i);
sleep(1);
}
printf("end.");
return 0;
}
void new_op(int signum, siginfo_t *info, void *myact) {
printf("receive signal %d\n", signum);
for (int i = 0; i < 5; ++i) {
printf("signal processing: %d\n", i);
sleep(1);
}
printf("process quit.");
exit(0);
}
复制代码
运行结果:
1
2
3
receive signal 15
signal processing: 0
signal processing: 1
signal processing: 2
signal processing: 3
signal processing: 4
process quit.
复制代码
最后,欢迎关注微信公众号,Let's go!
![]()