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

父进程与4个子进程通信的C程序阻塞问题求助

管道阻塞问题排查与修复

核心阻塞原因

父进程读取子进程返回数据的while(read(...))循环会一直挂起,因为父进程未关闭返回管道(pipes[0])的写端。管道的read操作返回EOF的前提是所有写端都被关闭,而父进程中pipes[0][1]始终处于打开状态,导致read永远等不到EOF,循环无法正常退出。

其他代码问题

  1. 子进程管道关闭逻辑混乱:子进程仅关闭了部分管道的读端,未处理不需要的写端,且判断条件错误,应关闭所有与当前子进程无关的管道两端。
  2. 父进程发送数据后未关闭子进程管道写端:父进程给子进程发完数据后未关闭对应写端,虽不会直接导致阻塞,但会造成资源泄漏。
  3. 错误处理缺失:pipe和fork的错误仅打印提示,未终止程序,可能引发后续逻辑执行异常。

修复后的代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char* argv[]) {

    int pipes[5][2], pid[4];

    // 创建管道并添加错误处理
    for (int i = 0; i < 5; i++) {
        if (pipe(pipes[i]) == -1) {
            perror("pipe failed");
            exit(EXIT_FAILURE);
        }
    }

    for (int i = 0; i < 4; i++) {
        pid[i] = fork();
        if (pid[i] == -1) {
            perror("fork failed");
            exit(EXIT_FAILURE);
        }
        if (pid[i] == 0) {
            // 子进程关闭所有无关管道
            for (int j = 0; j < 5; j++) {
                // 仅保留当前子进程需要的管道描述符
                if (j != i) close(pipes[j][0]);
                if (j != 0) close(pipes[j][1]);
            }
            // 关闭当前管道的写端(子进程无需写入)
            close(pipes[i][1]);

            int x;
            read(pipes[i][0], &x, sizeof(int));
            printf("Child %d received %d from parent\n", i + 1, x);
            x += (i + 1);
            write(pipes[0][1], &x, sizeof(int));
            printf("Child %d sent %d to parent\n", i + 1, x);

            // 关闭已使用的管道
            close(pipes[i][0]);
            close(pipes[0][1]);
            exit(EXIT_SUCCESS);
        } 
    }

    // 父进程提前关闭不需要的管道读端
    for (int j = 1; j < 5; j++) {
        close(pipes[j][0]);
    }

    printf("Input a number: ");
    int y, z;
    if (scanf("%d", &y) != 1) {
        perror("scanf failed");
        exit(EXIT_FAILURE);
    }

    // 给子进程发送数据并立即关闭对应写端
    for (int j = 0; j < 4; j++) {
        write(pipes[j][1], &y, sizeof(int));
        printf("Writing %d to child %d\n", y, j + 1);
        close(pipes[j][1]);
    }

    // 关闭返回管道的写端(关键:让read循环能正常退出)
    close(pipes[0][1]);

    // 读取子进程返回的数据
    while (read(pipes[0][0], &z, sizeof(int)) > 0) {
        printf("Received %d\n", z);
    }

    // 关闭返回管道的读端
    close(pipes[0][0]);

    // 等待所有子进程结束
    for (int i = 0; i < 4; i++) {
        wait(NULL);
    }

    return 0;
}

关键修复点说明

  1. 父进程关闭返回管道写端:在开始读取子进程返回数据前,父进程必须关闭pipes[0][1],这样当所有子进程都关闭该写端后,read会返回EOF,循环正常退出。
  2. 子进程清理无关管道:子进程仅保留自身需要的管道描述符,关闭其他所有管道的两端,避免资源泄漏。
  3. 父进程发完数据即关写端:发送完数据后立即关闭对应子进程管道的写端,既符合编程规范,也能让子进程的read操作正常结束。

内容的提问来源于stack exchange,提问作者Marco Lau

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 19:15:05