三十二、Linux 进程与信号——不可靠信号
32.1 不可靠信号问题
- 发生信号时关联动作被重置为默认设置
- 信号可能丢失
- 程序片段
- 在进入 sig_int 与再次调用 signal 之间发生的 SIGINT 信号将不会捕获
- 导致进程终止
以前版本会由这个问题,当前的 Linux 版本是安全的
1 #include <signal.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 6 void sig_handler(int signo) 7 { 8 if(signo == SIGINT){ 9 printf("process the SIGINT\n"); 10 sleep(5); 11 printf("%d catch SIGINT\n", getpid()); 12 printf("process the SIGINT finished\n"); 13 } 14 15 16 if(signo == SIGTSTP){ 17 printf("process the SIGTSTP\n"); 18 sleep(5); 19 printf("%d catch SIGTSTP\n", getpid()); 20 printf("process the SIGTSTP finished\n"); 21 } 22 } 23 24 int main(void) 25 { 26 if(signal(SIGINT, sig_handler) == SIG_ERR){ 27 perror("signal sigint error"); 28 } 29 30 if(signal(SIGTSTP, sig_handler) == SIG_ERR){ 31 perror("signal sigtstp error"); 32 } 33 34 printf("begin running\n"); 35 36 while(1) pause(); ///< 进程暂停等待信号 37 38 printf("end running\n"); 39 return 0; 40 }
对于连续发送相同信号,会进行延迟处理,发送查过 2 次以上不会处理。此机制与内核有关。
- 无法暂时阻塞信号
- 只能忽略信号
- 信号可能被丢失
- 程序片段
- 检测 sig_int_flag 变量和调用 pause 函数之间有个时间窗口
- 如果再该时间窗口内发生 SIGINT 信号?
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <signal.h> 5 6 int is_sig_occ = 0; 7 8 void sig_handler(int signo) 9 { 10 printf("signo occured: %d\n", signo); 11 is_sig_occ = 1; 12 } 13 14 15 int main(void) 16 { 17 if(signal(SIGINT, sig_handler) == SIG_ERR){ 18 perror("signal sigint error"); 19 } 20 21 printf("begin running\n"); 22 while(is_sig_occ == 0){ 23 sleep(5); 24 pause(); ///< 进程暂停等待信号发生 25 } 26 printf("I will running\n"); 27 return 0; 28 }
5s 之内执行的代码,pause后面的程序不会再执行,必须再发送一个信号后才可以执行。
建议将依赖于信号而执行的代码放置到信号处理函数中执行,否则这些代码可能不会执行
32.2 信号的特点
- 信号的发生是随机的,但信号在何种条件下发生是可预测的
- 进程刚开始启动时,所有信号的处理方式要么默认,要么忽略,忽略是 SIGUSR1 和 SIGUSR2 两个信号,其他都采取默认方式(大多数是终止进程)
- 进程在调用 exec 函数后,原有信号的捕捉函数失效
- 子进程的诞生总是继承父进程的信号处理方式
- 在系统层面上,信号的发生是可靠的,
- 在Linux 中的可靠性只保证依次,进程在处理信号期间若发生同类型的信号不会丢失(内核会保留),会被延迟处理,但同类型信号的多次发生只会保留一次,即被处理一次。
- 若不同类型的信号发生,也会被内核保留直接会被处理,处理完后,再处理原有信号