【问题标题】:Piping into a child process into another child process将一个子进程连接到另一个子进程
【发布时间】:2016-01-15 18:02:46
【问题描述】:

我正在尝试创建两个管道,第一个的输入是父进程的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);
    }
}

【问题讨论】:

  • while(read = getline(&line, &n, in) != -1) ---> while((read = getline(&line, &n, in)) != -1) 添加括号
  • 不,没有改变任何东西
  • 除了错误的terence,你还应该将变量'read'传递给你的write调用,而不是'n'。 'n' 是分配的空间量,一般比实际读取的量大,导致一些相同的数据被重复写入。这就是为什么您没有看到 terence 的建议有任何变化。这两个更改为我修复了输出,但我还没有弄清楚你的 wait() 问题。
  • 不错的收获!不幸的是,它并没有解决问题,在我调用 exec 后,孩子们似乎没有做任何事情......但 exec 也没有抛出任何错误。
  • 我用 /bin/cat 替换了你的 mapper 和 reducer 程序,以简化故障排除。我正在正确地回显输入,但是第一个 cat 进程随后挂起,试图从管道中读取更多数据(我正在运行 strace 以查看系统调用)。

标签: c linux pipe fork system-calls


【解决方案1】:

问题在于,当您在dup 之后有多个fd's 时,您必须在完成发送EOF 后关闭原始dup

简而言之,FD 的引用计数会随着 dup 而增加。

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

解决方案是从父进程创建管道和分支,并稍作重组。

特别感谢帮助我的 Russell Reed。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-05-08
    • 1970-01-01
    • 1970-01-01
    • 2013-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多