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

时间:2016-01-15 18:02:46

标签: 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);
    }
}

1 个答案:

答案 0 :(得分:1)

问题是,当您在fd's之后有多个dup时,您必须在完成dup后关闭原始版本和新版EOF发送。

简而言之,对于FDdup引用计数会递增。

另一个问题是我的进程树是线性的,而不是一个进程的两个子进程,所以主进程在输出之前退出,导致bash在执行似乎完成后有输出,使得它看起来像是挂起时它不是。

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

特别感谢帮助我的Russell Reed。