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

c Pipes causing program to hang in some cases

我正在尝试使用管道将 1 个命令的标准输出 link 执行到另一个命令的标准输入。例如模仿 (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 | 运行 程序时 | wc 作为我的执行程序,我的程序挂在第二个执行程序上。然而,当我使用一个简单的程序“./addone”作为我的 execs 时,整个执行正确完成。

其中"addone"是小程序:

#include<stdio.h>

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

有人建议我的问题可能是输入管道处于打开状态,但我无法弄清楚会发生什么,并且它不能解释为什么我的程序 运行 在使用时完美无缺我的简单测试程序。

任何关于导致此挂起的原因的建议将不胜感激。

我不知道你的问题是否解决了,但是继续评论,如果你要构建一个管道,关键是要理解如果你在一个进程中写入一个管道,你必须在下一个进程中从同一管道读取,如果要继续管道,则写入第二个管道。现在您可以将过程自动化,如 link C Minishell Adding Pipelines 中所示,但出于学习目的,如果您只研究单个线性示例,可能更容易理解。

下面是使用两个文件描述符 fd1fd2 实现的代码。您的 addone 进程将首先从普通的旧 stdin 读取,因此不会为读取操作文件描述符。但是,对于它的写入,它必须使用 fd1write-end(因为缺少更好的词)将其输出通过管道传输到下一个 child过程。

下一个 child 进程必须从同一个 fd1input-end 中读取以接收其输入,并且它必须写入output-end 另一个文件描述符 fd2。在两个 child 退出后, parent 进程必须从哪里读取?? (最后的管道也写的是什么...?啊哈!)fd2。所以 parent 重复 fd2read-end (关闭所有其他未使用的末端)并阅读多少 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;
}

例子Use/Output

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

仔细阅读,如果您有任何问题,请告诉我。一旦你明白了,它就有意义了。如果你暂时不用它,别担心,你会 re-learn 重新开始 :)