三十二、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 中的可靠性只保证依次,进程在处理信号期间若发生同类型的信号不会丢失(内核会保留),会被延迟处理,但同类型信号的多次发生只会保留一次,即被处理一次。
    • 若不同类型的信号发生,也会被内核保留直接会被处理,处理完后,再处理原有信号
posted @ 2018-12-30 21:53  游戏进行中  阅读(561)  评论(0编辑  收藏  举报