You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.25 14:45:04