Linux信号程序多次运行cpt返回值不一致问题原因分析
问题复现代码
你用于测试Linux信号机制的代码如下:
#include <signal.h> #include <unistd.h> #include <stdio.h> #include <sys/wait.h> #include <stdlib.h> int cpt = 0; void handler (int sig) { cpt++ ; } int main() { int i; signal(SIGCHLD, handler); for (i = 0; i < 5; i++) { if (fork() == 0) { exit(0); } } while (wait(NULL) != -1) ; printf("cpt = %d\n", cpt); return 0; }
你预期程序固定输出cpt = 5,但实际运行时会随机返回3、4、5三种结果。
根本原因
输出随机波动的核心原因是:Linux下编号1~31的标准非实时信号不支持排队机制。当同一种信号处于待处理状态(即信号已经触发,但还没来得及递送到用户态执行注册的处理函数)时,无论后续该信号又触发了多少次,内核都只会保留1个待处理信号实例,不会记录该信号的实际触发总次数,多触发的信号会被直接合并丢弃。
对应代码的具体执行逻辑:
- 你通过
signal()注册了SIGCHLD信号的处理函数,按照机制设计,每个子进程终止时内核都会向父进程发送SIGCHLD信号通知。 - 循环创建的5个子进程没有额外业务逻辑,调用
exit(0)后会立刻终止,多个子进程很可能在极短的时间窗口内先后退出。如果前一个SIGCHLD信号还没完成递送、还没执行handler累加逻辑,后续到达的同类型SIGCHLD信号不会逐个排队等待处理,只会被合并标记为“存在待处理SIGCHLD”,最终只会触发一次handler执行,cpt也就只会累加1次。 - 程序每次运行时,操作系统的进程调度时序、信号递送的具体时机都是不确定的:如果5次SIGCHLD触发时都没有出现待处理状态的重叠,没有信号被合并,最终就输出5;如果有1次合并(两次信号触发重叠被合并为一次)就输出4;如果有2次合并就输出3,这就是结果随机的来源。
需要注意的是,代码末尾的while (wait(NULL) != -1)逻辑确实可以正确回收全部5个退出的子进程,但回收子进程的动作不会补触发那些被合并丢弃的SIGCHLD信号,因此cpt的计数永远只等于实际被递送到用户态执行处理函数的信号次数,和子进程退出的总次数没有必然的相等关系。
内容的提问来源于stack exchange,提问作者Rafik Bouloudene
相关产品推荐
相关产品推荐

