学习管道,执行,分叉,并尝试将三个进程链接在一起

时间:2014-05-02 00:56:07

标签: c linux exec fork pipe

我正在学习使用管道并在pipes上跟随此代码。该程序使用fork生成两个子进程。第一个孩子跑步' ls'命令和输出到pipe1。从pipe1运行的第二个读取' wc'并输出到stdout。

我只是试图在中间添加第三个进程,从pipe1读取并输出到pipe2。基本上我正在尝试做什么

  ls | cat | wc -l

我想做的事情:

(ls)stdout -> pipe1 -> stdin(cat)stdout-> stdin(wc -l) -> stdout

什么都没有打印到stdout,程序永远不会退出。

这是我的代码,其中包含对流程#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);
}

1 个答案:

答案 0 :(得分:2)

问题是你没有在流程3中关闭pfd[1],在案例0之后添加close(pfd[1]);,因为流程3会修复它。

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

  1. 流程0

    这是主要流程,此过程中的pfd[1]将在wait()之前关闭。

  2. 流程1

    ls完成后

    ,此过程中的pfd[1]将由操作系统自动关闭。

  3. 流程2

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

  4. 流程3

    pfd[1]正在运行时,

    wc在此过程中处于打开状态,这就是当时发生的事情:

    1. 在流程2中,cat尝试阅读pfd[0]来自pfd[1]的数据
    2. 在流程3中,wc尝试阅读pfd2[0]来自pfd2[1]的数据
    3. 因为pdf[1]仍在进程3中打开,并且没有任何内容会被写入,所以在进程2(cat)中从pfd[0]读取将永远等待
    4. 因为进程3中的cat仍然存在,所以在进程3(wc)中从pfd2[0]读取将等待(永远)
  5. 如您所见,由于文件描述符泄漏,您在进程2(cat)和进程3(wc)之间存在死锁。要打破这种僵局,您只需在流程3中关闭pfd[1],然后再运行wc

      进程2中的
    1. cat将在进程1中的ls退出后退出,因为没有任何内容(cat)可以阅读
    2. 在流程2中的cat退出后,流程3中的wc也将退出,因为没有任何内容(wc)可以阅读
    3. 之后,主进程(父进程)将退出,程序将完成。

    4. 管道的读取端可能有多个写入结束,除非所有这些写入结束都关闭,文件结束将不会传送到读取端,读者只会等待获得更多数据。如果没有任何事情要发生,那读者将永远等待。