转:https://blog.csdn.net/SLN2432713617/article/details/89138731
解释信号丢失问题:
阻塞,sigaction 函数有阻塞的功能,比如 SIGINT 信号来了,进入信号处理函数,默认情况下,在信号处理函数未完成之前,如果又来了一个 SIGINT 信号,其将被阻塞,只有信号处理函数处理完毕,才会对后来的 SIGINT 再进行处理,同时后续无论来多少个 SIGINT,仅处理一个 SIGINT,sigaction 会对后续 SIGINT 进行排队合并处理。
原文:https://blog.csdn.net/beginning1126/article/details/8680757
- 连续给一个进程发送多个相同信号时,部分信号丢失而未得到处理(使用 signal 函数处理信号),代码如下:
/* test2.c */ #include <stdio.h> #include <unistd.h> #include <signal.h> // 信号处理函数使用的全局变量 int count_sig = 0; void handleSig(int); void main(){ printf("main running...\n"); signal(SIGINT, handleSig); // 循环等待信号的到来 while(1){ sleep(1); printf("alive.\n"); } printf("done.\n"); } // 信号处理函数 void handleSig(int sig){ sleep(1); count_sig ++; printf("got signal. c:%d\n", count_sig); }
运行程序,执行结果如下(使用键盘向进程连续 发送 3 个终端中断信号,却 只有 2 个信号被处理函数处理 了):
ubuntu@cuname:~/dev/beginning-linux-programming/test$ gcc -o test2 test2.c ubuntu@cuname:~/dev/beginning-linux-programming/test$ ./test2 main running... ^C^C^Cgot signal. c:1 // 连续发送 3 个终端中断信号,却只有 2 个信号被处理 got signal. c:2 alive. alive. alive. alive. alive. Killed // 终止该进程:‘ kill -sigkill 39237 ’
再次运行该程序,结果如下(这次 连续发送更多的信号,并查看输出情况,发现***仅仅 2 个信号被处理***):
ubuntu@cuname:~/dev/beginning-linux-programming/test$ ./test2 main running... ^C^C^C^C^C^C^C^C^C^C^Cgot signal. c:1 // 连续发送更多的信号时,仅仅 2 个信号被处理 got signal. c:2 alive. alive. alive. Killed // kill 该进程
这是什么原因呢?
可能和 Linux 信号处理机制有关
- 实验:在一个信号处理(使用 signal 函数)过程期间,给进程发送另一个不同类型的信号:
/* test4.c */ #include <stdio.h> #include <unistd.h> #include <signal.h> #include <errno.h> int count_sig = 0; void handleSig(int); void main(){ printf("main running...\n"); signal(SIGINT, handleSig); // 处理 SIGINT 终端中断信号 signal(SIGALRM, handleSig); // 处理 SIGALRM 闹钟信号 // 循环等待信号 while(1){ sleep(1); printf("alive.\n"); } printf("done.\n"); } // 信号处理函数 void handleSig(int sig){ if(sig == SIGINT){ // 处理 SIGINT 终端中断信号 count_sig ++; printf("got signal: sigint. c:%d\n", count_sig); errno = 0; sleep(20); printf("got signal: sigint. done, sleep errno code:%d\n", errno); }else if(sig == SIGALRM){ // 处理 SIGALRM 闹钟信号 printf("got signal: sigalrm. done\n"); } }
执行结果如下(在第一个 信号 SIGINT 处理期间,(发送 SIGALRM 信号,使得当前的信号处理函数被中断)进程转而执行 SIGALRM 信号处理函数,在处理完第二个信号(SIGALRM)后,进程又返回到第一个信号处理函数中断时的位置,但是并没有重入 sleep 函数,而是从 sleep 中立即返回(sleep 函数调用的错误为 4),紧接着继续执行下面的 printf 语句):
其中 sleep 函数的 errno code 错误码为 4 :Interrupted system call(被中断的系统调用),在睡眠期间,被第二个信号(SIGALRM)打断。
从运行结果可以看出,两个信号处理函数交织在一起执行:SIGINT 的处理函数还未运行结束时就收到 SIGALRM 信号,导致函数执行被中断。
- 使用 sigaction 函数(解决信号处理函数的竞态问题),改写实验 2 中的 signal 函数部分,观察区别,代码如下:
/* test5.c */ #include <stdio.h> #include <unistd.h> #include <signal.h> #include <errno.h> int count_sig = 0; void handleSig(int); void main(){ printf("main running...\n"); struct sigaction act; act.sa_handler = handleSig; sigemptyset(&act.sa_mask); // 将信号 SIGALRM 加入到进程屏蔽字中, // 在 SIGINT 信号处理期间,接收到的 SIGALRM 信号会排队直到 SIGINT 信号处理完毕, // !信号屏蔽 不是遗弃信号,被屏蔽的信号会被排队,并在随后得到处理! sigaddset(&act.sa_mask, SIGALRM); act.sa_flags = SA_RESTART; sigaction(SIGINT, &act, 0); sigaction(SIGALRM, &act, 0); while(1){ sleep(1); printf("alive.\n"); } printf("done.\n"); } void handleSig(int sig){ if(sig == SIGINT){ count_sig ++; printf("got signal: sigint. c:%d\n", count_sig); errno = 0; int res = sleep(20); printf("got signal: sigint. done, sleep errno code:%d\n", errno); }else if(sig == SIGALRM){ printf("got signal: sigalrm. done\n"); } }
执行结果如下(信号被排队,依次处理):
ubuntu@cuname:~/dev/beginning-linux-programming/test$ gcc -o test5 test5.c ubuntu@cuname:~/dev/beginning-linux-programming/test$ ./test5main running... alive. alive. ^Cgot signal: sigint. c:1 got signal: sigint. done, sleep errno code:0 // sleep 调用未被中断 got signal: sigalrm. done // SIGINT 信号处理完毕后,SIGALRM 信号得到处理 alive. alive. got signal: sigalrm. done // 单独发送 SIGALRM 信号 alive. alive. ^Cgot signal: sigint. c:2 // 再次尝试 got signal: sigint. done, sleep errno code:0 got signal: sigalrm. done alive. alive. Killed // kill 该进程
实际运用中,需要对不同到signal设定不同的到信号处理函数,SIG_IGN忽略/SIG_DFL默认,这俩宏也可以作为信号处理函数。同时SIGSTOP/SIGKILL这俩信号无法捕获和忽略。注意,经过实验发现,signal函数也会堵塞当前正在处理的signal,但是没有办法阻塞其它signal,比如正在处理SIG_INT,再来一个SIG_INT则会堵塞,但是来SIG_QUIT则会被其中断,如果SIG_QUIT有处理,则需要等待SIG_QUIT处理完了,SIG_INT才会接着刚才处理。
标签:信号处理,printf,SIGINT,alive,信号,Linux,signal From: https://www.cnblogs.com/ainingxiaoguai/p/16995764.html