学习pipes、exec、fork,并尝试将三个流程链接在一起

学习pipes、exec、fork,并尝试将三个流程链接在一起,c,linux,exec,fork,pipe,C,Linux,Exec,Fork,Pipe,我正在学习使用管道,并遵循上面的代码。该程序使用fork生成两个子进程。第一个子级运行“ls”命令并输出到pipe1。第二个从pipe1读取的数据运行“wc”,并输出到stdout 我试图在中间添加第三个过程,从PIPE1读取并输出到PIPE2。基本上就是我想做的 ls | cat | wc -l 我想做的是: (ls)stdout -> pipe1 -> stdin(cat)stdout-> stdin(wc -l) -> stdout 任何东西都不会打印到标准

我正在学习使用管道,并遵循上面的代码。该程序使用fork生成两个子进程。第一个子级运行“ls”命令并输出到pipe1。第二个从pipe1读取的数据运行“wc”,并输出到stdout

我试图在中间添加第三个过程,从PIPE1读取并输出到PIPE2。基本上就是我想做的

  ls | cat | wc -l
我想做的是:

(ls)stdout -> pipe1 -> stdin(cat)stdout-> stdin(wc -l) -> stdout
任何东西都不会打印到标准输出,程序也不会退出

这是我对流程3所做更改的代码


问题是您没有在流程3中关闭
pfd[1]
,添加
close(pfd[1])在该过程中的情况0之后,3将修复它

在过程3中,
cat
将从
pfd[0]
读取,但是在这些过程中有四个
pfd[1]

  • 进程0

    这是主进程,
    pfd[1]
    在此进程中,将在
    wait()
    之前关闭该进程

  • 过程1

    ls
    完成后,操作系统将自动关闭此过程中的
    pfd[1]

  • 过程2

    pfd[1]
    在执行
    cat
    之前已关闭

  • 过程3

    int
    main(int argc, char *argv[])
    {
        int pfd[2];                                     /* Pipe file descriptors */
        int pfd2[2];
    
        if (pipe(pfd) == -1)                            /* Create pipe */
            perror("pipe");
    
        if (pipe(pfd2) == -1)                            /* Create pipe */
            perror("pipe");
    
        /*
        Fork process 1 and exec ls command
        write to pfd[1], close pfd[0]
        */
        switch (fork()) {
        case -1:
            perror("fork");
    
        case 0:             
            if (close(pfd[0]) == -1)                   
                perror("close 1");
    
            // dup stdout on pfd[1]
            if (pfd[1] != STDOUT_FILENO) {            
                if (dup2(pfd[1], STDOUT_FILENO) == -1)
                    perror("dup2 2");
                if (close(pfd[1]) == -1)
                    perror("close 4");
            }
            execlp("ls", "ls", (char *) NULL);          
            perror("execlp ls");
        default:            
            break;
        }
    
        /*
        *   Fork process 2 and exec wc command
        read from pfd[0], close pfd[1]
        write to pfd[1], close pfd2[0]
        */
        switch (fork()) {
        case -1:
            perror("fork");
        case 0:  
            // read from pfd[0]           
            if (close(pfd[1]) == -1)                  
                perror("close 3");
    
            if (pfd[0] != STDIN_FILENO) {            
                if (dup2(pfd[0], STDIN_FILENO) == -1)
                    perror("dup2 2");
                if (close(pfd[0]) == -1)
                    perror("close 4");
            }
    
            if (pfd2[1] != STDOUT_FILENO) {            
                if (dup2(pfd2[1], STDOUT_FILENO) == -1)
                    perror("dup2 2");
                if (close(pfd2[1]) == -1)
                    perror("close 4");
            }
    
            execlp("cat", "cat", (char *) NULL);
            perror("execlp cat");
        default:
            break;
        }
    
        /*
        *   Fork process 3
        */
        switch (fork()) {
        case -1:
            perror("fork");
        case 0:          
            if (close(pfd2[1]) == -1)                  
                perror("close 3");
    
            if (pfd2[0] != STDIN_FILENO) {            
                if (dup2(pfd2[0], STDIN_FILENO) == -1)
                    perror("dup2 2");
                if (close(pfd2[0]) == -1)
                    perror("close 4");
            }
    
            execlp("wc", "wc", "-l", (char *) NULL);
            perror("execlp wc");
        default:
            break;
        }
    
    
        /* Parent closes unused file descriptors for pipe, and waits for children */
    
        if (close(pfd[0]) == -1)
            perror("close 5");
        if (close(pfd[1]) == -1)
            perror("close 6");
        if (close(pfd2[0]) == -1)
            perror("close 5");
        if (close(pfd2[1]) == -1)
            perror("close 6");
    
        if (wait(NULL) == -1)
            perror("wait 1");
        if (wait(NULL) == -1)
            perror("wait 2");
        if (wait(NULL) == -1)
            perror("wait 3");
    
        exit(EXIT_SUCCESS);
    }
    
    pfd[1]
    wc
    运行时在此过程中打开,这就是当时发生的情况:

  • 在过程2中,
    cat
    尝试从
    pfd[1]
    读取数据
  • 在过程3中,
    wc
    尝试从
    pfd2[1]
    读取数据
  • 由于进程3中的
    pdf[1]
    仍处于打开状态,并且不会写入任何内容,因此从进程2(cat)中的
    pfd[0]
    读取将永远等待
  • 由于进程3中的
    cat
    仍处于活动状态,因此从进程3(wc)中的
    pfd2[0]
    读取将等待(永远)
  • 如您所见,由于文件描述符泄漏,进程2(cat)和进程3(wc)之间存在死锁。要打破此死锁,只需在运行
    wc
    之前关闭流程3中的
    pfd[1]
    ,然后:

  • 进程2中的
    cat
    将在进程1中的
    ls
    退出后退出,因为没有剩余内容可供其(cat)读取
  • 在进程2中的
    cat
    退出后,进程3中的
    wc
    也将退出,因为它(wc)已无可读取的内容
  • 之后,主进程(父进程)将退出,程序将完成


  • 管道的读端可能有多个写端,除非所有这些写端都关闭,否则文件的结尾不会传递到读端,读卡器只会等待更多数据的到来。如果没有什么事情发生,读者将永远等待。

    这解决了问题,谢谢。但我仍然不确定它为什么起作用。我想我需要更多地了解一下管道的工作原理。进程3的stdin或stdout都不使用pfd,因此我认为不需要关闭它。@JDD管道的读取端可能有多个写入端,除非所有这些写入端都关闭,否则文件的结尾将不会传递到读取端,而读取器将只等待更多数据的到来。如果没有什么事情发生,这就是你的情况,读者将永远等待。我明白了。我不知道他们是那样工作的。谢谢+1,但我认为一个更干净的解决方案是在使用管道的进程分支之后关闭默认情况下的管道(父级)。通常,创建尽可能靠近fork的管道,并尽快将其关闭;它不会退出。您的案例语句将报告错误,然后进入下一个案例,这不是您想要的。