C语言两子进程向父进程发信号,父进程回复时遇死锁/等待锁问题
问题分析与修复方案
看起来你的代码里有几个关键问题导致了死锁和未定义行为,我来一步步拆解并修复:
核心问题梳理
- 子进程层级错误:你现在是让第一个子进程再fork出第二个子进程,导致第二个是父进程的孙子进程,父进程无法直接管理它(比如
waitpid会失败) - 信号发送方向错误:子进程没有向父进程发送指定的SIGUSR1/SIGUSR2,反而给其他子进程发SIGTERM,完全不符合需求
- 管道时序颠倒:子进程先阻塞在
read(),根本没机会发送信号给父进程,直接造成死锁 - 父进程信号等待逻辑缺失:只调用一次
pause(),无法等待两个子进程的信号 - 异步信号不安全操作:信号处理函数里用
printf,可能引发未定义行为
修复后的完整代码
#include <stdio.h> #include <signal.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <stdlib.h> #include <string.h> // 异步信号安全的打印函数 void safe_printf(const char *msg) { write(STDOUT_FILENO, msg, strlen(msg)); } // 信号处理函数,仅做标记(避免不安全操作) volatile sig_atomic_t sigusr1_received = 0; volatile sig_atomic_t sigusr2_received = 0; void handler(int signumber) { if (signumber == SIGUSR1) { sigusr1_received = 1; safe_printf("Received SIGUSR1\n"); } else if (signumber == SIGUSR2) { sigusr2_received = 1; safe_printf("Received SIGUSR2\n"); } } int main() { // 注册信号处理函数 signal(SIGUSR1, handler); signal(SIGUSR2, handler); // 为每个子进程创建独立管道(避免读写冲突) int pipe1[2], pipe2[2]; if (pipe(pipe1) == -1 || pipe(pipe2) == -1) { perror("Pipe creation failed"); exit(EXIT_FAILURE); } pid_t child1, child2; // 创建第一个子进程 child1 = fork(); if (child1 == 0) { // 子进程1:关闭管道写端(只读) close(pipe1[1]); // 睡眠3秒后向父进程发SIGUSR1 safe_printf("Child 1: Waiting 3s, will send SIGUSR1 to parent\n"); sleep(3); kill(getppid(), SIGUSR1); // 等待父进程的回复 char buf[100] = {0}; ssize_t bytes_read = read(pipe1[0], buf, sizeof(buf)-1); if (bytes_read > 0) { safe_printf("Child 1 received: "); safe_printf(buf); safe_printf("\n"); } close(pipe1[0]); safe_printf("Child 1 exited\n"); exit(EXIT_SUCCESS); } // 创建第二个子进程 child2 = fork(); if (child2 == 0) { // 子进程2:关闭管道写端(只读) close(pipe2[1]); // 睡眠3秒后向父进程发SIGUSR2 safe_printf("Child 2: Waiting 3s, will send SIGUSR2 to parent\n"); sleep(3); kill(getppid(), SIGUSR2); // 等待父进程的回复 char buf[100] = {0}; ssize_t bytes_read = read(pipe2[0], buf, sizeof(buf)-1); if (bytes_read > 0) { safe_printf("Child 2 received: "); safe_printf(buf); safe_printf("\n"); } close(pipe2[0]); safe_printf("Child 2 exited\n"); exit(EXIT_SUCCESS); } // 父进程逻辑:等待两个信号都收到 safe_printf("Parent: Waiting for signals from children...\n"); while (!sigusr1_received || !sigusr2_received) { pause(); // 阻塞直到收到信号 } // 向两个子进程分别发送回复 close(pipe1[0]); // 关闭父进程的读端 close(pipe2[0]); const char *msg1 = "Hello from parent to Child 1"; const char *msg2 = "Hello from parent to Child 2"; write(pipe1[1], msg1, strlen(msg1)+1); // 包含结束符 write(pipe2[1], msg2, strlen(msg2)+1); // 关闭写端,让子进程read()收到EOF close(pipe1[1]); close(pipe2[1]); // 等待两个子进程退出 int status; waitpid(child1, &status, 0); waitpid(child2, &status, 0); safe_printf("Parent process exited\n"); return 0; }
关键修改说明
- 子进程创建方式:父进程直接fork两次,创建两个独立的子进程,确保父进程持有两个子进程的PID,能正常等待它们退出。
- 独立管道:给每个子进程分配一个单独的管道,避免读写冲突,确保每个子进程能收到专属的回复。
- 信号逻辑修正:子进程先发送信号给父进程(用
getppid()获取父进程PID),再等待管道回复,颠倒了之前的时序,解决死锁。 - 信号等待逻辑:用两个原子变量标记信号是否收到,父进程循环
pause()直到两个信号都到达。 - 异步信号安全:替换
printf为write实现的safe_printf,避免信号处理函数中的不安全操作。 - 管道正确关闭:父进程写完后立即关闭管道写端,让子进程的
read()能正常返回,避免无限阻塞。
运行效果
编译运行后,你会看到类似如下输出:
Parent: Waiting for signals from children... Child 1: Waiting 3s, will send SIGUSR1 to parent Child 2: Waiting 3s, will send SIGUSR2 to parent Received SIGUSR1 Received SIGUSR2 Child 1 received: Hello from parent to Child 1 Child 1 exited Child 2 received: Hello from parent to Child 2 Child 2 exited Parent process exited
内容的提问来源于stack exchange,提问作者Shapperd




