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

使用pipe()实现任意数量子进程间整数传递的问题求解

C语言使用pipe实现多子进程链式管道通信问题

我希望通过pipe()在多个子进程之间传递数据,效果如下:
使用管道通信的多个子进程
该实现需要支持任意数量的子进程,要求子进程打印从管道读取到的数值,将数值加1后传递给下一个进程。

目前遇到以下3个问题:

  • 子进程函数被以相同参数多次调用
  • 该问题导致文件描述符发生非预期变更
  • 除第一个进程外,其余所有进程操作时errno返回Bad file descriptor

第一个进程运行一切正常,可以从父进程接收数值,向writePipe写入时也没有报错。

程序运行的控制台输出如下:

id:0 pipeRead:5 PipeWrite:8
id:0 res:4 value:500
id:1 pipeRead:9 PipeWrite:12
id:2 pipeRead:13 PipeWrite:16
id:1 pipeRead:3 PipeWrite:6
id:2 pipeRead:7 PipeWrite:10
id:1 res:-1 value:0
id:2 pipeRead:3 PipeWrite:6
id:2 res:-1 value:0
Read Error: Bad file descriptor
Read Error: Bad file descriptor

原代码实现如下:

int execChild(int appID,  int pipeRead[2], int pipeWrite[2]){
    //Before start process, I close read/write that not used here
    //Close write of read pipe
    close(pipeRead[1]);
    //Close read of write pipe
    close(pipeWrite[0]);

    printf("id:%d pipeRead:%d PipeWrite:%d\n",appID,pipeRead[0],pipeWrite[1]);

    int buff=0;
    long int res;
    res = read(pipeRead[0],&buff,sizeof(int));
    if (res==-1) perror("Read Error");
    //Close read pipe after used
    close(pipeRead[0]);

    printf("id:%d res:%ld value:%d\n",appID,res,buff);
    //Increment value
    buff += 1;

    res = write(pipeWrite[1],&buff,sizeof(int));
    if(res==-1)perror("Write Error");
    //Close write pipe after used
    close(pipeWrite[1]);
    return 0;
}

int main(int argc, char *argv[]) {
    int nbFis = 3;

    //Init array of pipeArray with malloc
    int* pipeArr[nbFis];
    for (int i = 0; i < nbFis; i++)
        pipeArr[i] = (int*)malloc(2 * sizeof(int));
    
    for (int i = 0; i < nbFis; i++) // Generate n child
    {
        //Here we open every inter child pipe
        //If the process is 0 we need to open the last pipe of the array
        //Else we open the last pipe
        if(i==0){
            pipe(pipeArr[nbFis-1]);//Open Read Pipe
            //To test I inject an int value here to child 0
            int value = 500;
            write(pipeArr[nbFis-1][1],&value,sizeof(int));//Write value in the pipe
        }else{
            pipe(pipeArr[i-1]);//Open Read Pipe
        }
        pipe(pipeArr[i]);//Open Write Pipe
        
        if (fork() == 0) {
            //If it's the first child we send the last pipe in the array as read pipe and current pipe as write pipe
            //Else we send the previous pipe in the list as read pipe and current pipe as write pipe
            if(i==0){
                execChild(i,pipeArr[nbFis-1],pipeArr[i]);
            }else{
                execChild(i,pipeArr[i-1],pipeArr[i]);
            }
        }
    }

    //Wait for the end of process and free memory
    for (int i = 0; i < nbFis; i++) {
        free(pipeArr[i]);
        wait(NULL);
    }
    return 0;
}

问题原因

  1. 子进程未主动退出:子进程调用完execChild后没有调用exit(),会继续执行外层的for循环逻辑,重复创建更多子进程,这是子进程函数被多次调用的核心原因。
  2. 管道创建逻辑错误:在循环创建子进程的过程中重复创建管道,同一个管道索引的文件描述符会被多次覆盖,导致后续子进程拿到的fd是已失效的错误值。
  3. 无用文件描述符未关闭:父子进程都没有关闭自身用不到的管道端,管道引用计数异常,既会导致fd泄露,也会引发读写操作异常。

修复后代码

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

int execChild(int appID, int pipeRead[2], int pipeWrite[2]){
    close(pipeRead[1]);
    close(pipeWrite[0]);

    printf("id:%d pipeRead:%d PipeWrite:%d\n", appID, pipeRead[0], pipeWrite[1]);

    int buff = 0;
    ssize_t res;
    res = read(pipeRead[0], &buff, sizeof(int));
    if (res == -1) perror("Read Error");
    close(pipeRead[0]);

    printf("id:%d res:%ld value:%d\n", appID, (long)res, buff);
    buff += 1;

    res = write(pipeWrite[1], &buff, sizeof(int));
    if(res == -1) perror("Write Error");
    close(pipeWrite[1]);
    return 0;
}

int main(int argc, char *argv[]) {
    int nbFis = 3;
    // 提前一次性创建所有需要的管道
    int pipeArr[nbFis][2];
    for (int i = 0; i < nbFis; i++) {
        if(pipe(pipeArr[i]) == -1) {
            perror("pipe create error");
            exit(1);
        }
    }

    // 写入初始值到第一个子进程的读管道
    int value = 500;
    write(pipeArr[nbFis-1][1], &value, sizeof(int));
    close(pipeArr[nbFis-1][1]);

    for (int i = 0; i < nbFis; i++) {
        pid_t pid = fork();
        if (pid == -1) {
            perror("fork error");
            exit(1);
        }
        if (pid == 0) {
            // 子进程先关闭所有用不到的管道
            for(int j=0; j<nbFis; j++) {
                if(i == 0) {
                    if(j != nbFis-1 && j != 0) {
                        close(pipeArr[j][0]);
                        close(pipeArr[j][1]);
                    }
                } else {
                    if(j != i-1 && j != i) {
                        close(pipeArr[j][0]);
                        close(pipeArr[j][1]);
                    }
                }
            }
            if(i == 0){
                execChild(i, pipeArr[nbFis-1], pipeArr[i]);
            }else{
                execChild(i, pipeArr[i-1], pipeArr[i]);
            }
            // 子进程执行完成直接退出,不执行后续父进程逻辑
            exit(0);
        }
    }

    // 父进程关闭所有管道端
    for(int i=0; i<nbFis; i++) {
        close(pipeArr[i][0]);
        close(pipeArr[i][1]);
    }

    // 等待所有子进程退出
    for (int i = 0; i < nbFis; i++) {
        wait(NULL);
    }
    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 18:18:02