【问题标题】:Multiple pipe implementation using system call fork() execvp() wait() pipe() - it is simply not working使用系统调用 fork() execvp() wait() pipe() 实现多管道 - 它根本不起作用
【发布时间】:2012-10-10 11:28:43
【问题描述】:

我需要实现我的外壳来处理多个管道命令。例如我需要能够处理这个:ls | grep -i cs340 | sort | uniq | cut -c 5。我假设问题是我没有将上一个命令的输出传递给下一个命令的输入。 当我执行我的代码时,它没有给我任何输出。我正在使用这个伪代码:

for cmd in cmds
    if there is a next cmd
        pipe(new_fds)
    fork
    if child
        if there is a previous cmd
            dup2(old_fds[0], 0)
            close(old_fds[0])
            close(old_fds[1])
        if there is a next cmd
            close(new_fds[0])
            dup2(new_fds[1], 1)
            close(new_fds[1])
        exec cmd || die
    else
        if there is a previous cmd
            close(old_fds[0])
            close(old_fds[1])
        if there is a next cmd
            old_fds = new_fds
if there are multiple cmds
    close(old_fds[0])
    close(old_fds[1])

这里是处理多个管道的函数的源代码。

void execute_multiple_commands(struct command ** commands_to_exec,
        int num_commands_p)
{
    pid_t status;
    int i, err;
    int new_fd[2], old_fd[2];
    pid_t pid, cpid;

    // creating child process
    if ( (cpid = fork()) == -1)
    {
        fprintf(stderr, "Could not create child process, exiting...");
        exit(1);
    }

    if (cpid == 0) // in the child process we run multiple pipe handling
    {
        for (i = 0; i < num_commands_p; i++) // for each cmd in cmds
        {
            if (i+1 < num_commands_p) // if there is next cmd
                pipe(new_fd);

            if ( (pid = fork()) == -1)
            {
                fprintf(stderr, "Could not create child process, exiting...");
                exit(1);
            }

            if (pid == 0) // if child
            {
                if (i != 0) // if there is a previous command
                {
                    dup2(old_fd[0], 0); // setting up old_pipe to input into the child
                    close(old_fd[0]);
                    close(old_fd[1]);

                }
                if (i+1 < num_commands_p) // if there is a next cmd
                {
                    close(new_fd[0]); // setting up new_pipe to get output from child
                    dup2(new_fd[1], 1);
                    close(new_fd[1]);

                    err = execvp(commands_to_exec[i]->args[0], commands_to_exec[i]->args);
                    status = err;
                    exit(err);
                }
            }
            else
            {
                waitpid(pid, &status, 0);
                if (status == -1)
                    exit(1);

                if (i != 0) // if there a previous command
                {
                    close(old_fd[0]);
                    close(old_fd[1]);
                }
                if (i+1 < num_commands_p) // if there a next cmd
                {
                    old_fd[0] = new_fd[0];
                    old_fd[1] = new_fd[1];
                }
                exit(0);
            } // end if
        } // end for

        if (i) // if there a multiple commands
        {
            close(old_fd[0]);
            close(old_fd[1]);
        }
    }
    else // in the parent process we are waiting for child to handle multiple pipes
        waitpid(cpid, &status, 0);
}

函数execvp() 采用结构数组。我检查了我所有的解析部分,它工作正常。这是我遇到问题的execute_multiple_commands() 函数。

结构体代码如下:

// name: command
// desc: holds one command (meaning that it can be
//        more than one token in that command)
//        "ls -la" will be an example of one command
//       holds num of tokens in command array
struct command
{
    char ** args;
    int num_args;
};

【问题讨论】:

    标签: c shell fork pipe waitpid


    【解决方案1】:

    我建议一个新策略,R2:

    function do(commands)
        if commands is of size 1
            exec commands[0] || die
        split commands into c1 (first command) c2 (the rest of them)
        open
        if fork 
            close input end of pipe
            dup output of pipe to stdin
            do (c2) || die
        close output end of pipe
        dup input of pipe to stdout
        exec c1 || die
    

    使用递归函数,尤其是在您维护列表时,将帮助您简化逻辑。您不必担心这里的堆栈深度,因为无论如何您的整个地址空间都会被覆盖。

    其他新闻,来自man page

    从这些系统调用之一成功返回后,旧的和 新的文件描述符可以互换使用。他们指的是 相同的打开文件描述(参见 open(2)),从而共享文件偏移量 和文件状态标志;例如,如果文件偏移量由 在其中一个描述符上使用 lseek(2),偏移量也会改变 为另一个。

    当您说要关闭管道的两端时,这意味着什么?你真的要关闭它——它和你的程序打算使用的标准输入/输出。

    --> 稍后编辑

    正如 Jonathan Leffler 所指出的,上述信息是正确的。我已经通过以下程序确认了这一点:

    #include <unistd.h>
    
    int main(){
        dup2(0, 7);
        write(7, "Hey, 1\n", 7);
        close(0);
        write(7, "Hey, 2\n", 7);
        close(7);
        write(7, "Hey, 3\n", 7);
    }
    

    这会导致以下输出:

    $ gcc dup2Test.c && ./a.out
    Hey, 1
    Hey, 2
    

    谢谢,乔纳森!

    【讨论】:

    • 您对dup2() 手册页的解释不正确。一般来说,如果您有一个管道(两个文件描述符)并且您使用 dup()dup2() 将一端映射到您的标准输入或标准输出,那么您应该关闭 both 文件描述符pipe() 确保正确生成 EOF。 close() 关闭一个打开的文件描述符;如果没有其他文件描述符引用它,它只会关闭打开的文件描述。但是dup2() 为您留下了两个引用相同打开文件描述的打开文件描述符。 (是的,描述符与描述令人困惑!)
    猜你喜欢
    • 2014-05-31
    • 2010-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-12
    • 1970-01-01
    • 2021-07-23
    • 1970-01-01
    相关资源
    最近更新 更多