摘要:
- 在信号处理函数执行时,会阻塞当前信号。
- 当信号处理函数返回时,系统会帮我们把刚刚阻塞的信号再从阻塞集中移除。
一、 临时阻塞特性
当执行信号处理函数的时候,会临时将当前被处理信号阻塞。为了能说明问题,采用实验来验证。
下面这段程序在收到 SIGALRM 信号或者 SIGQUIT 信号时,会打印当前信号,同时打印当前被阻塞的信号。
测试代码:
#include <unistd.h>
#include <signal.h>
#include <setjmp.h>
#include <stdio.h>void printBlock() {sigset_t block;sigprocmask(SIG_BLOCK, NULL, &block);printf("block:");if (sigismember(&block, SIGQUIT)) printf("SIGQUIT, ");if (sigismember(&block, SIGALRM)) printf("SIGALRM\t");puts("");
}void handler(int sig) {if (sig == SIGQUIT) printf("SIGQUIT, ");if (sig == SIGALRM) printf("SIGALRM, ");printBlock();puts("--------------------------------------------------");
}int main() {printf("I'm %d\n", getpid());signal(SIGQUIT, handler);signal(SIGALRM, handler);printf("before signal, ");printBlock();while (1) {pause();}return 0;
}
输出结果:
结果分析:
可以看到当程序收到 SIGALRM 信号时,在信号处理函数中 SIGALRM 信号会被临时阻塞;SIGQUIT 信号同理。
执行完毕再从阻塞信号集中移除我就不验证了。实际上,你重复发送 SIGALRM 信号,信号处理函数都能正常执行就已经证明 SIGALRM 信号未被阻塞(当然了,如果你手速比 cpu 运行的还快,可能会有信号合并的情况,这种不在考虑之列)。
二、跳出你的信号处理函数
试想一下,如果你在信号处理函数中使用 longjmp 跳走了,系统会不会帮你把临时阻塞的信号给撤走?答案是,不会。这将导致程序 bug。从此以后,你的程序再也不能处理该信号了。空口无凭,用实验说话。
下面这段程序,在信号处理函数中打印当前信号和当前被阻塞的信号。完成后没有直接 return,而是使用 longjmp 跳转到 setjmp 的位置。接下来再打印阻塞信号。
测试代码:
#include <unistd.h>
#include <signal.h>
#include <setjmp.h>
#include <stdio.h>jmp_buf jmpbuf;void printBlock()
{sigset_t block;sigprocmask(SIG_BLOCK, NULL, &block);printf("block:");if (sigismember(&block, SIGQUIT)) printf("SIGQUIT, ");if (sigismember(&block, SIGALRM)) printf("SIGALRM");puts("");
}void handler(int sig)
{if (sig == SIGQUIT) printf("SIGQUIT, ");if (sig == SIGALRM) printf("SIGALRM, ");printBlock();longjmp(jmpbuf, 1);puts("--------------------------------------------------");
}int main()
{printf("I'm %d\n", getpid());signal(SIGQUIT, handler);signal(SIGALRM, handler);printf("before signal, ");printBlock();if (setjmp(jmpbuf) != 0) {printf("jump to here! ");printBlock();puts("====================================================");}while (1) {pause();}return 0;
}
输出结果:
结果分析:
可以看到,当 jump 到 main 函数中的时候,SIGQUIT 依然被阻塞了,SIGALRM 也是。此后,无论你再怎么发送这两个信号,处理函数也不会执行了。因为这两个信号没有从阻塞集中删除。
这是 bug。
解决方案是使用另外两个函数,分别是 sigsetjmp 和 siglongjmp。所以,我们只要把上面代码中的 setjmp 和 longjmp 分别替换成这两个函数就行了。
三、函数sigsetjump和siglongjump应用
1. 测试代码:
#include <unistd.h>
#include <signal.h>
#include <setjmp.h>
#include <stdio.h>jmp_buf jmpbuf;void printBlock() {sigset_t block;sigprocmask(SIG_BLOCK, NULL, &block);printf("block:");if (sigismember(&block, SIGQUIT)) printf("SIGQUIT, ");if (sigismember(&block, SIGALRM)) printf("SIGALRM");puts("");
}void handler(int sig) {if (sig == SIGQUIT) printf("SIGQUIT, ");if (sig == SIGALRM) printf("SIGALRM, ");printBlock();siglongjmp(jmpbuf, 1); puts("--------------------------------------------------");
}int main() {printf("I'm %d\n", getpid());signal(SIGQUIT, handler);signal(SIGALRM, handler);printf("before signal, ");printBlock();if (sigsetjmp(jmpbuf, 1) != 0) {printf("jump to here! ");printBlock();puts("====================================================");}while(1) {pause();}return 0;
}
输出结果:
四、参考资料
1. 47-跳出信号处理函数