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

复用管道处理多消息:父进程与子进程通信异常排查

问题分析与修复方案

先梳理你代码里的核心问题和修复点:


核心阻塞原因:错误使用wait(NULL)

你的子进程是无限循环(while(true)),永远不会调用exit退出,而父进程每次发送消息后调用wait(NULL),会一直阻塞等待某个子进程终止——但子进程根本不会结束,这直接导致父进程卡住,无法回到输入流程。

其他关键问题

  • 子进程未清理无关管道描述符:fork后子进程继承了所有4个管道的读写句柄,除了自己管道的读端,其他所有管道的读写句柄都要关闭,否则管道无法正确触发EOF,还会造成资源泄漏。
  • 未处理read返回值:当管道写端被关闭时,read会返回0表示无数据可读,此时子进程应该退出循环,而不是继续死等。
  • 变量未定义/未初始化:query未声明、rand()未初始化(需要srand(time(NULL))生成随机数)、fd[t][W]中的t是未定义变量。

修正后的完整代码

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

#define R 0
#define W 1

int main(int argc, char const *argv[]) {
    char query[100]; // 声明query变量
    int fd[4][2];

    // 初始化随机数种子
    srand(time(NULL));

    // 创建4个管道,增加错误处理
    for(int i = 0; i < 4; i++){
        if(pipe(fd[i]) == -1){
            perror("pipe failed");
            exit(EXIT_FAILURE);
        }
    }

    pid_t fils_pid[4];

    // 创建4个子进程
    for(int i = 0; i < 4; i++){
        if((fils_pid[i] = fork()) == 0){
            // 子进程:关闭自己管道的写端,关闭其他所有管道的读写端
            close(fd[i][W]);
            for(int j = 0; j < 4; j++){
                if(j != i){
                    close(fd[j][R]);
                    close(fd[j][W]);
                }
            }

            char buf[100];
            ssize_t bytes_read;
            // 处理read返回值,直到管道关闭(返回0)
            while((bytes_read = read(fd[i][R], buf, sizeof(buf)-1)) > 0){
                buf[bytes_read] = '\0'; // 确保字符串结束
                printf("子进程%d处理:Query: %s", i+1, buf);
            }

            close(fd[i][R]);
            exit(EXIT_SUCCESS);
        } else if(fils_pid[i] == -1){
            perror("fork failed");
            exit(EXIT_FAILURE);
        }
    }

    // 父进程:关闭所有管道的读端
    for(int i = 0; i < 4; i++){
        close(fd[i][R]);
    }

    // 父进程循环获取输入并发送
    while(true){
        int idx = rand() % 4;
        printf("Enter a query: ");
        // 处理fgets的错误或EOF(比如Ctrl+D终止输入)
        if(fgets(query, sizeof(query), stdin) == NULL){
            break;
        }
        // 发送数据,包含字符串结束符
        write(fd[idx][W], query, strlen(query) + 1);
        // 移除原来的wait(NULL),因为子进程不会主动退出
    }

    // 父进程退出前关闭所有管道写端,让子进程收到EOF后退出
    for(int i = 0; i < 4; i++){
        close(fd[i][W]);
    }

    // 等待所有子进程退出
    for(int i = 0; i < 4; i++){
        waitpid(fils_pid[i], NULL, 0);
    }

    printf("Bye bye!\n");
    return 0;
}

修复说明

  1. 去掉了父进程中的wait(NULL),改为在程序退出前用waitpid等待所有子进程结束,避免不必要的阻塞。
  2. 子进程清理了所有无关的管道描述符,避免资源泄漏和管道状态异常。
  3. 处理了read的返回值,当管道写端关闭时,子进程会退出循环并终止。
  4. 补全了未定义的变量,初始化了随机数种子,增加了管道创建、fork失败的错误处理。

内容的提问来源于stack exchange,提问作者Tomás Guija Valiente

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 17:15:54