c - 将一个子进程管道化为另一个子进程

标签 c linux pipe fork system-calls

我正在尝试创建两个管道,第一个管道的输入是父进程的 argv[1] 中的输入文件的内容逐行传输到 mapper 进程做一些工作,然后最终进入 reducer 进程减少它。

当我在 `bash 中像这样运行我的 mapperreducer 时:

./mapper < input.txt | reducer

它运行完美,但下面的程序什么都不输出并卡在 wait(NULL);

我的代码

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

void checkForkError(pid_t pid);
void mapperSetup(int mapperPipe[]);
void reducerSetup(int reducerPipe[]);

int main(int argc, char* argv[]) {
    if(argc < 2) {
        printf("please specify an input file\n");
        exit(1);
    }
    int mapperPipe[2]; //last index write end, first index read end

    if (pipe(mapperPipe) == -1) {
           perror("error piping");
           exit(EXIT_FAILURE);
    }

    pid_t firstChild = fork();

    checkForkError(firstChild);

    if(firstChild == 0) { //child
        mapperSetup(mapperPipe);
    }
    else {
        close(mapperPipe[0]);
        close(STDOUT_FILENO);
        dup(mapperPipe[1]);
        FILE* in = fopen(argv[1], "r");
        if(in == NULL) {
            perror("error opening file");
            exit(EXIT_FAILURE);
        }
        ssize_t read;
        size_t n;
        char* line = NULL;
        while(read = getline(&line, &n, in) != -1) {
            write(STDOUT_FILENO, line, n);
        }
        close(STDOUT_FILENO);
        free(line);
        wait(NULL);
    }
}

void inline checkForkError(pid_t pid) {
    if(pid < 0) {
        perror("error forking!!!");
    }
}

void mapperSetup(int mapperPipe[]) {
    int reducerPipe[2];

    if(pipe(reducerPipe) == -1) {
        perror("error piping");
        exit(EXIT_FAILURE);
    }

    pid_t secondChild = fork();

    checkForkError(secondChild);
    if(secondChild == 0) { //reducer process
        reducerSetup(reducerPipe);
    }
    else { //mapper process
        close(mapperPipe[1]); //close write end
        close(STDIN_FILENO); //close stdin
        dup(mapperPipe[0]); //dup pipe out to stdin

        close(reducerPipe[0]); //close read end
        close(STDOUT_FILENO); //close stdout
        dup(reducerPipe[1]); //dup output to reducer pipe

        if(execv("mapper", (char *[]){"mapper", NULL}) == -1) {
            perror("exec error");
            exit(EXIT_FAILURE);
        }
    }
}

void reducerSetup(int reducerPipe[]) {
    close(reducerPipe[1]); //close write end of second pipe
    close(STDIN_FILENO); //close stdin
    dup(reducerPipe[0]); //dup read end of pipe to stdin

    if(execv("reducer", (char *[]){"reducer", NULL}) != -1) {
        perror("exec error");
        exit(EXIT_FAILURE);
    }
}

最佳答案

问题是,当您在 dup 之后有多个 fd 时,您必须关闭原始的以及新的 dup完成发送 EOF

简而言之,FD 引用计数针对 dup 递增。

另一个问题是我的进程树是线性的,不是一个进程的两个子进程,所以主进程在输出之前就退出了,导致bash在执行完之后还有输出,让它看起来像是挂了它不是。

解决方案是通过稍作重构从父进程创建管道和分支。

特别感谢帮助我的 Russell Reed。

关于c - 将一个子进程管道化为另一个子进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34817079/

相关文章:

在 C 中创建一个管道

c - 使用 for 循环的二维数组赋值

c - 双指针作为 execvp() 的参数

c - 给定一个4位数字,如何在屏幕上打印该数字的个位、十、百、千?

linux - 如何确定 telnet 命令是否成功

c - X 秒后终止程序 Linux C

c++ - 无法在 NetBeans 的 Linux 中用 C++ 和 OpenGL (GLFW) 编译简单的源代码

c++ - 使用 select 多路复用未命名管道和其他文件描述符

c - 使用 Fork 和 Dup 的 Unix 管道

c - 一个很好的应用程序,可以看到多线程的好处