Linux下C语言多进程信号程序异常:仅父进程输出问题排查
C语言多进程信号同步异常问题分析
需求与问题
需要实现的逻辑:
- 创建两个子进程
- 第二个子进程阻塞,直到接收父进程的SIGUSR1信号
- 第一个子进程阻塞,直到接收第二个子进程的SIGUSR2信号(该信号终止它)
- 父进程在所有子进程终止后再退出
实际执行异常:仅父进程的printf内容被输出,子进程的打印和信号逻辑未按预期执行。
用户提供的代码
#include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/wait.h> #include <signal.h> void this(int sig) { printf("this is this"); } int main() { int pid = fork(); int pid2; if (pid < 0) { exit(-1); } else if (pid == 0) { printf("FIrst child is paused"); pause(); printf("ERror"); } else { pid2 = fork(); if (pid2 < 0) { exit(-2); } else if (pid2 == 0) { signal(SIGUSR1, &this); printf("Second child is paused"); pause(); kill(pid,SIGUSR2); printf("signal sent to first child"); } else { printf("this is the parent"); kill(pid2, SIGUSR1); printf("signal sent to second child"); wait(NULL); exit(-3); } } }
问题根源分析
1. 标准输出缓冲未刷新
printf默认采用行缓冲模式,当输出内容没有换行符\n时,数据会暂存在进程的输出缓冲区中,不会立即打印到终端。子进程可能在缓冲区未刷新时就进入阻塞或终止状态,导致内容无法显示。
2. 信号发送与处理函数注册的竞争条件
父进程在创建第二个子进程后,立即调用kill(pid2, SIGUSR1)发送信号,但此时第二个子进程可能还没来得及执行signal(SIGUSR1, &this)注册信号处理函数。SIGUSR1的默认处理动作是终止进程,这会导致第二个子进程直接退出,后续向第一个子进程发送SIGUSR2的逻辑完全无法执行,第一个子进程会一直卡在pause()。
3. 父进程未回收所有子进程
父进程只调用了一次wait(NULL),只能回收一个子进程,另一个子进程会变成僵尸进程,直到父进程退出后才被系统回收。
修正后的代码
#include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/wait.h> #include <signal.h> void sigusr1_handler(int sig) { printf("this is sigusr1 handler\n"); } int main() { int pid = fork(); int pid2; if (pid < 0) { perror("fork first child failed"); exit(EXIT_FAILURE); } else if (pid == 0) { printf("First child is paused\n"); pause(); // SIGUSR2默认会终止进程,无需额外处理 printf("Error: First child shouldn't reach here\n"); exit(EXIT_FAILURE); } else { pid2 = fork(); if (pid2 < 0) { perror("fork second child failed"); exit(EXIT_FAILURE); } else if (pid2 == 0) { signal(SIGUSR1, sigusr1_handler); printf("Second child is paused\n"); pause(); kill(pid, SIGUSR2); printf("signal sent to first child\n"); exit(EXIT_SUCCESS); } else { printf("this is the parent\n"); // 短暂休眠,确保第二个子进程完成信号处理函数注册 sleep(1); kill(pid2, SIGUSR1); printf("signal sent to second child\n"); // 回收所有子进程 int status; while (wait(&status) > 0); exit(EXIT_SUCCESS); } } }
关键修改说明
- 所有
printf添加\n,强制刷新输出缓冲区,确保内容及时显示 - 父进程发送信号前添加
sleep(1),避免竞争条件(生产环境可使用更可靠的同步机制,如管道、信号量) - 父进程用循环
wait回收所有子进程,避免僵尸进程 - 优化错误提示,使用
perror打印fork失败的具体原因 - 重命名信号处理函数为更具语义的
sigusr1_handler,提升代码可读性
内容的提问来源于stack exchange,提问作者scanpower
相关产品推荐
相关产品推荐

