我正在学习使用管道,并在pipes上跟随这段代码。程序使用fork生成两个子进程。第一个子进程运行'ls'命令并输出到pipe1。第二次从pipe1读取运行'wc'并输出到stdout。
我只想在中间添加第三个进程,从pipe1读取并输出到pipe2。基本上我想做的就是

  ls | cat | wc -l

我要做的是:
(ls)stdout -> pipe1 -> stdin(cat)stdout-> stdin(wc -l) -> stdout

从来没有打印到STDUT,程序永远不会退出。
这是我的代码和流程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);
}

最佳答案

问题是您没有在流程3中关闭pfd[1],在流程3中的案例0之后添加close(pfd[1]);将修复它。
在过程3中,cat将从pfd[0]读取,但是在这些过程中有四个pfd[1]
进程0
这是主进程,pfd[1]在此进程中将在wait()之前关闭。
流程1
完成ls后,操作系统将自动关闭此过程中的pfd[1]
过程2
pfd[1]在执行cat之前已关闭。
过程3
pfd[1]在此进程中处于打开状态,而wc正在运行,这就是当时发生的情况:
在过程2中,cat尝试读取pfd[0]中的数据。
在过程3中,pfd[1]尝试读取wc中的数据。
因为pfd2[0]在进程3中仍然打开,并且不会写入任何内容,所以从进程2中的pfd2[1]读取(cat)将永远等待
因为进程3中的pdf[1]仍然有效,所以从进程3中的pfd[0]读取(wc)将等待(永远)
如您所见,由于文件描述符泄漏,进程2(cat)和进程3(wc)之间存在死锁。要打破此死锁,只需在运行cat之前关闭进程3中的pfd2[0],然后:
过程2中的pfd[1]将在过程1退出后wc退出,因为它(CAT)没有任何内容可读。
在过程2退出cat之后,过程3中的ls也将退出,因为它(WC)没有剩下什么可以读取。
之后,主进程(父进程)将退出,程序将结束。
管道的读端可能有多个写端,除非所有这些写端都已关闭,否则文件端将不会传递到读端,读取器将只等待更多数据的到来。如果什么都没有,那读者将永远等待。

10-08 16:22