C叉和管道多个过程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C叉和管道多个过程相关的知识,希望对你有一定的参考价值。

我正在尝试使用系统调用fork和pipe在C中实现此命令cat /etc/passwd | grep 1000 | cut -d: -f1。 当我只使用两个命令cmd1 | cmd2所以1叉它工作正常但是当我使用超过2个进程时问题发生 这是我的代码,我认为问题出在祖父块中

更新: 我关闭了一些未封闭的文件描述符

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


int main(){
    int fd[2], fd2[2];
    pid_t pid, pid2;
    char str[100];

    if(pipe(fd) != 0){
        fprintf(stderr, "Error pipe.\n");
        return 1;
    }
    if(pipe(fd2) != 0){
        fprintf(stderr, "Error pipe.\n");
        return 1;
    }

    do{
        pid = fork();
    }while(pid == -1 && errno == EAGAIN);

    if(pid == -1){
        perror("fork()");
        exit(1);
    }

    if(pid == 0){
        pid2 = fork();
        if(pid2 == 0){
            // fprintf(stderr, "1 - Grand Child\n");
            close(fd[0]);
            dup2(fd[1], 1);
            close(fd[1]);
            execlp("cat", "cat", "/etc/passwd", NULL);
            perror("cat error");
        }else{
            wait(&pid2);
            // fprintf(stderr, "2 - Child\n");
            close(fd[1]);
            dup2(fd[0], 0);
            close(fd[0]);

            up2(fd2[1], 1);
            close(fd2[1]);

            execlp("grep", "grep", "1000", NULL);
            perror("grep failed");
        }
        return 1;
    }else{
        wait(&pid);
        // fprintf(stderr, "3 - Parent\n");
        close(fd[0]);
        dup2(fd2[0], 0);
        close(fd2[0]);
        execlp("cut", "cut", "-d:", "-f1", NULL);
        perror("cut failed");
        return 1;
    }
    return 0;
}
答案

您需要关闭子项和父项中所有未使用的文件描述符!在这种情况下,使用dup2()时,可能会关闭与管道关联的所有文件描述符。

为清楚起见,我将宏用于与stdin和stdout相关联的文件描述符。宏STDIN_FILENO和STDOUT_FILENO在unistd.h中声明。

不需要等待孩子(wait(&pid);wait(&pid2);)。在运行cat之前,你不需要等待grep结束,因为grep会在它们到来时处理它们。管道应该有一个很大的缓冲区,比如65536字节,所以在cat命令结束后,数据将被缓冲在管道中,但它不是值得信赖的东西。

以下代码只有很小的改动,有效:

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

int main(){
    int fd[2], fd2[2];
    pid_t pid, pid2;
    char str[100];

    if(pipe(fd) != 0){
        fprintf(stderr, "Error pipe.\n");
        return 1;
    }
    if(pipe(fd2) != 0){
        fprintf(stderr, "Error pipe.\n");
        return 1;
    }

    do{
        pid = fork();
    }while(pid == -1 && errno == EAGAIN);

    if(pid == -1){
        perror("fork()");
        exit(1);
    }

    if(pid == 0){
        pid2 = fork();
        if(pid2 == 0){
            // fprintf(stderr, "1 - Grand Child\n");
            dup2(fd[1], STDOUT_FILENO); // 1);
            close(fd[0]);
            close(fd[1]);
            close(fd2[1]);
            close(fd2[0]);

            execlp("cat", "cat", "/etc/passwd", NULL);
            perror("cat error");
        }else{
            // wait(&pid2);
            // fprintf(stderr, "2 - Child\n");
            dup2(fd[0], STDIN_FILENO); //0);
            dup2(fd2[1], STDOUT_FILENO); // 1);
            close(fd[0]);
            close(fd[1]);
            close(fd2[1]);
            close(fd2[0]);

            execlp("grep", "grep", "1000", NULL);
            perror("grep failed");
        }
        return 1;
    }else{
        // wait(&pid);
        // fprintf(stderr, "3 - Parent\n");
        dup2(fd2[0], STDIN_FILENO); //0);
        close(fd[0]);
        close(fd[1]);
        close(fd2[1]);
        close(fd2[0]);

        execlp("cut", "cut", "-d:", "-f1", NULL);
        perror("cut failed");
        return 1;
    }
    return 0;
}

以上是关于C叉和管道多个过程的主要内容,如果未能解决你的问题,请参考以下文章

15种Python片段去优化你的数据科学管道

C shell中的多个管道

渲染管道光栅阶段一“总览”

渲染管道光栅阶段一“总览”

C多个进程写入1个管道

使用循环在 C 中管道两个或多个 shell 命令