c在某些情况下导致程序挂起的管道

时间:2016-05-14 06:16:15

标签: c unix pipe pipeline

我尝试使用管道将1个命令exec的stdout链接到另一个命令的stdin。例如模仿(cmd1 | cmd2)

以下是我的代码的严重剥离版本。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

int main (int argC, char *argv[])
{

    //Run first command
    int fds[2];
    if (pipe (fds) < 0) {       //Create pipe
        fprintf (stderr, "Pipe Failed\n");
    }

    int pid;
    if ((pid = fork ()) == -1) {
        fprintf (stderr, "Fork 1 Failed\n");
        exit (1);
    }

    if (pid == 0) {             //First child proccess
        close (fds[0]);         //Close input end of pipe
        dup2 (fds[1], STDOUT_FILENO);   //Set stdout to output pipe
        close (fds[1]);         //Close output end of pipe

        fprintf (stderr, "Exec 1 executing now\n");
        execlp ("./addone", "./addone", NULL);  //execute first command - Doesnt cause hang
        //execlp("ls", "ls", NULL);//Causes hang
        fprintf (stderr, "Exec 1 failed\n");
    } else {                    //First parent segment
        int returnStatus;
        waitpid (pid, &returnStatus, 0);        //Wait for child 1 to finish

        fprintf (stderr, "Back to parent 1\n");
    }

    //Run second command
    if ((pid = fork ()) == -1) {
        fprintf (stderr, "Fork 2 failed\n");
    }

    if (pid == 0) {             //second child proccess
        dup2 (fds[0], STDIN_FILENO);    //Set stdin to input pipe
        close (fds[0]);         //Close input end of pipe
        close (fds[1]);         //Close output end of pipe

        fprintf (stderr, "Exec 2 executing now\n");
        execlp ("./addone", "./addone", NULL);  //execute first command - Doesnt cause hang
        //execlp("wc", "wc", NULL);//Causes hang
        fprintf (stderr, "Exec 2 failed\n");
    } else {                    //second parent segment
        int returnStatus;
        waitpid (pid, &returnStatus, 0);        //Wait for child 2 to finish

        fprintf (stderr, "Back to parent 2\n");
        //Done with pipes
        close (fds[0]);
        close (fds[1]);
    }
    return 0;
}

在程序中我尝试制作一个管道并将第一个execs stdout路由到秒stdin。尝试使用ls |运行程序时作为我的执行官,我的程序挂在第二个执行官身上。但是当我使用一个简单的程序&#34; ./ addone&#34;作为我的执行官,整个执行都正确完成。

其中&#34; addone&#34;是一个小程序:

#include<stdio.h>

int main(){
    int value = 0;
    scanf("%d", &value);
    value++;
    printf("%d\n", value);
}

有人提出我的问题可能是因为输入管道处于打开状态,但我无法解决可能发生的情况,并且它无法解释为什么我的程序在使用我的简单测试程序时运行完美。

任何有关导致此挂起的建议都将不胜感激。

1 个答案:

答案 0 :(得分:0)

我不知道你的问题是否得到解决,但是从评论继续,如果你要构建一个管道,关键是要理解如果你在一个进程中写一个管道,你必须阅读在下一个进程中从同一个管道中,如果要继续管道,则写入第二个管道。现在,您可以按照链接C Minishell Adding Pipelines中所示自动执行该过程,但出于学习目的,如果您只是使用单个线性示例,则可能更容易理解。

以下是使用两个文件描述符fd1fd2实现您的代码。您的addone进程将首先从普通的stdin读取,因此不会对其读取操作文件描述符。但是,对于它的写入,它必须使用fd1 write-end (缺少更好的单词)将其输出传递给下一个子进程。

下一个子进程必须从同一fd1输入端读取以接收其输入,并且必须写入 output-end 另一个文件描述符fd2。两个孩子退出后,父进程必须从哪里读取? (最后写的管道是什么......?啊!)fd2。因此,父级会复制fd2读取端(关闭所有其他未使用的端点),并阅读最终答案,其中addone添加到初始编号中。

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

int main (void) {

    int fd1[2], fd2[2];  /* file descriptors 1 & 2 */
    int pid;

    if (pipe (fd1) < 0 || pipe (fd2) < 0) { /* open both pipes */
        fprintf (stderr, "pipe creation failed\n");
    }

    if ((pid = fork ()) == -1) {
        fprintf (stderr, "fork 1 failed\n");
        exit (1);
    }

    if (pid == 0) {                     /* first child */
        dup2 (fd1[1], STDOUT_FILENO);   /* dup write-end of 1st */
        close (fd1[0]);                 /* close all others */
        close (fd2[0]);
        close (fd2[1]);

        fprintf (stderr, "Exec 1 executing now\n");
        execlp ("./addone", "./addone", NULL);
        fprintf (stderr, "Exec 1 failed\n");
    }
    else {
        int returnStatus;
        waitpid (pid, &returnStatus, 0);
        fprintf (stderr, "Back to parent 1\n");
    }

    if ((pid = fork ()) == -1) {
        fprintf (stderr, "Fork 2 failed\n");
    }

    if (pid == 0) {                     /* second child */
        dup2 (fd1[0], STDIN_FILENO);    /* dup read-end of 1st  */
        dup2 (fd2[1], STDOUT_FILENO);   /* dup write-end of 2nd */
        close (fd1[1]);                 /* close all others */
        close (fd2[0]);

        fprintf (stderr, "Exec 2 executing now\n");
        execlp ("./addone", "./addone", NULL);
        fprintf (stderr, "Exec 2 failed\n");
    }
    else {
        int returnStatus;
        waitpid (pid, &returnStatus, 0);
        fprintf (stderr, "Back to parent 2\n");
    }

    dup2 (fd2[0], 0);   /* dup read-end of 2nd */
    close (fd2[1]);     /* close all others */
    close (fd1[0]);
    close (fd1[1]);

    int total;
    scanf ("%d", &total);
    printf ("The total was: %d\n\n", total);

    close (fd2[0]);

    return 0;
}

示例使用/输出

$ echo 10 | ./bin/pipeaddone
Exec 1 executing now
Back to parent 1
Exec 2 executing now
Back to parent 2
The total was: 12

仔细看看,如果您有任何疑问,请告诉我。一旦你得到它,这是有道理的。如果您暂时不使用它,请不要担心,您将再次重新学习它:)