将多个管道连接在一起时遇到麻烦

问题描述

我正在尝试创建一个小型外壳程序,该程序可以接受多个命令并将它们链接在一起,最后执行它们。

到目前为止,我已经创建了一个列表来存储输入,并将它们保存到名为command的数组中,在这里我使用strtok将输入分割开并将结果放入数组中。因此,如果我输入cat -n filename.txt,则将cat放在command [0] -n到command [1],并将filename.txt放在command [2]。我还在每个命令的末尾添加了NULL(在我的cat示例中以command [3]结尾)。

我能够执行第一个命令,但是如果我尝试第二个命令,则第一个命令是唯一要执行的命令,因此我怀疑我的管道未正确链接

这是我到目前为止所做的:

  pid_t pid;

    int status;
    int pipes[number_arguments - 1][2];

    // Create pipes
    for (int i = 0; i < number_arguments - 1; i++)
    {
        if (pipe(pipes[i]))
        {
            perror("pipe");
            exit(EXIT_FAILURE);
        }
    }

    // Create new processes
    for (int i = 0; i < number_arguments; i++)
    {
        pid = fork();

        if (pid == -1)
        {
            perror("fork");
            exit(EXIT_FAILURE);
        }

        if (pid == 0)
        {
             // First child
            if (i == 0)
            {
                dup2(pipes[i][WRITE_END],STDOUT_FILENO);
            }

            //Middle child
            if ((i != 0) && ((i+1) != number_arguments))
            {
                dup2(pipes[i-1][READ_END],STDIN_FILENO);
                dup2(pipes[i][WRITE_END],STDOUT_FILENO);
            }

            // Last child
            if ((i+1) == number_arguments)
            {
                dup2(pipes[i][WRITE_END],STDOUT_FILENO);
            }

            // close all pipes
            for (int j = 0; j < number_arguments - 1; j++)
            {
                close(pipes[j][READ_END]);
                close(pipes[j][WRITE_END]);
            }

            if (execvp(command[0],command) < 0)
            {
                perror("exec failure");
                exit(EXIT_FAILURE);
            }
        }
        // Parent
        else
        {
            for (int j = 0; j < number_arguments - 1; j++)
            {
                close(pipes[j][READ_END]);
                close(pipes[j][WRITE_END]);
            }
            waitpid(pid,&status,0);
        }
    }

我很感谢我能得到的任何帮助。

很抱歉,如果我在发布此帖子时出错,这是我的第一篇帖子。

解决方法

您可以在命令启动循环之外正确创建管道,并正确关闭子级中的所有管道文件描述符。后者尤其是一个常见问题。

但是,在启动第一个孩子之后,您错误地使父进程关闭了所有管道,因此第二个及以后的孩子没有管道可以使用。您还需要等待管道中的每个孩子死亡,然后再启动下一个孩子-您需要这些孩子同时运行。

您需要父进程来启动所有子进程,然后才等待管道完成。

父进程将(可能)需要保留其所有子PID的记录。在启动所有子项之前,它一定不能关闭其管道副本(但是这样做很关键)。它将需要在waitpid()周围使用循环。它必须等到最后一个子项(管道中的最后一个进程)终止。它可能会或可能不会等到管道中所有较早的子项都终止之后。

您应该做的一件事,尤其是在出现问题时,是对系统调用的返回值进行错误检查。您会发现dup2()close()调用在第二次迭代中失败,并带有EBADF(错误的文件描述符)。即使一切正常,检查系统调用并在失败时提出适当的策略也不错。