C语言多次调用fork实现多进程管道传值时程序挂起问题排查
程序挂起原因定位
你遇到的死锁问题全部来自fork循环内部的逻辑书写错误:
- wait和return语句放错位置:主进程在第一次fork出子进程0后,会直接进入循环体内的wait循环等待子进程退出,根本不会继续循环创建剩下的2个子进程,也不会执行后续写初始值、读最终结果的逻辑。
- 此时子进程0正阻塞在
read(pipes[0][0])上,等待主进程写入初始数据;主进程正阻塞在wait上,等待子进程退出,双向等待直接导致程序永久挂起。 - 循环变量重名:wait的内层循环使用了和外层fork循环相同的计数变量
i,就算移走wait逻辑,也会覆盖外层循环计数,导致子进程创建不全。 - 打印逻辑错误:子进程写完数据后,打印语句错把
Sent写成了Got,和读操作的打印重复,不符合预期输出格式。
核心调整点
- 把所有
wait(NULL)逻辑移到主进程完成全部管道读写操作之后执行,不要放在fork循环内部。 - 删除fork循环内部的主进程
return 0语句,保证主进程能完成所有子进程的创建。 - 修正子进程写操作后的打印内容,匹配预期输出。
- 内层循环换用独立的计数变量,避免覆盖外层循环的计数。
修复后的完整可运行代码
#include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <stdlib.h> #define PROCESS_NUM 3 // 3个进程对应4个管道完成全链路连接 int main(int argc, char *argv[]) { int pids[PROCESS_NUM]; int pipes[PROCESS_NUM + 1][2]; int i; // 预创建所有管道 for (i = 0; i < PROCESS_NUM + 1; i++) { if (pipe(pipes[i]) == -1) { printf("创建管道失败\n"); return 1; } } // 循环创建所有子进程 for (i = 0; i < PROCESS_NUM; i++) { pids[i] = fork(); if (pids[i] == -1) { printf("创建进程失败\n"); return 2; } if (pids[i] == 0) { // 子进程关闭所有不需要的管道fd int j; for (j = 0; j < PROCESS_NUM + 1; j++) { // 仅保留当前进程对应管道的读端 if (i != j) { close(pipes[j][0]); } // 仅保留当前进程输出管道的写端 if (i + 1 != j) { close(pipes[j][1]); } } int x; // 读取上游传入的数据 if (read(pipes[i][0], &x, sizeof(int)) == -1) { printf("读取数据失败\n"); exit(3); } printf("(%d) Got %d\n", i, x); x++; // 自增后写入下游管道 if (write(pipes[i + 1][1], &x, sizeof(int)) == -1) { printf("写入数据失败\n"); exit(4); } printf("(%d) Sent %d\n", i, x); // 关闭当前进程持有的fd close(pipes[i][0]); close(pipes[i + 1][1]); return 0; } } // 主进程关闭所有不需要的管道fd int j; for (j = 0; j < PROCESS_NUM + 1; j++) { // 仅保留最后一个管道的读端 if (j != PROCESS_NUM) { close(pipes[j][0]); } // 仅保留0号管道的写端 if (j != 0) { close(pipes[j][1]); } } int y = 5; printf("Main process sent %d\n", y); // 写入初始值 if (write(pipes[0][1], &y, sizeof(int)) == -1) { printf("主进程写入失败\n"); return 4; } // 读取最终结果 if (read(pipes[PROCESS_NUM][0], &y, sizeof(int)) == -1) { printf("主进程读取失败\n"); return 3; } printf("The final result is %d\n", y); close(pipes[0][1]); close(pipes[PROCESS_NUM][0]); // 统一回收所有子进程 for (i = 0; i < PROCESS_NUM; i++) { wait(NULL); } return 0; }
运行输出
Main process sent 5 (0) Got 5 (0) Sent 6 (1) Got 6 (1) Sent 7 (2) Got 7 (2) Sent 8 The final result is 8
内容的提问来源于stack exchange,提问作者user10440380
相关产品推荐
相关产品推荐

