多重管道实现使用系统调用fork()的execvp()等待()管道() - 它根本不工作 [英] Multiple pipe implementation using system call fork() execvp() wait() pipe() - it is simply not working

查看:643
本文介绍了多重管道实现使用系统调用fork()的execvp()等待()管道() - 它根本不工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要实现我的shell能处理多种管道命令。例如,我需要能够处理这个问题: LS |用grep -i cs340 |排序| uniq的|切〜5 。我假设的问题是,我没有通过previous命令的输出到下一个命令的输入。
当我执行我的code,它给了我没有输出。我使用这种伪code:

 在CMDS CMD
    如果存在下一CMD
        管(new_fds)
    叉子
    如果孩子
        如果有一个previous CMD
            dup2(old_fds [0],0)
            关闭(old_fds [0])
            关闭(old_fds [1])
        如果存在下一CMD
            关闭(new_fds [0])
            dup2(new_fds [1],1)
            关闭(new_fds [1])
        EXEC CMD ||死
    其他
        如果有一个previous CMD
            关闭(old_fds [0])
            关闭(old_fds [1])
        如果存在下一CMD
            old_fds = new_fds
如果有多个CMDS
    关闭(old_fds [0])
    关闭(old_fds [1])

下面是处理多个管道的函数的源代码code。

 无效execute_multiple_commands(结构命令** commands_to_exec,
        INT num_commands_p)
{
    将为pid_t状态;
    INT I,走错了路。
    INT new_fd [2],old_fd [2];
    将为pid_t PID,慢性盆腔炎;    //创建子进程
    如果((CPID =叉())== -1)
    {
        fprintf中(标准错误,无法创建子进程,退出...);
        出口(1);
    }    如果(CPID == 0)//在子过程中,我们同时运行多个管道处理
    {
        对于(i = 0; I< num_commands_p;我++)//为每个CMDS CMD
        {
            如果第(i + 1< num_commands_p)如果有下一个CMD //
                管(new_fd);            如果((PID =叉())== - 1)
            {
                fprintf中(标准错误,无法创建子进程,退出...);
                出口(1);
            }            如果(PID == 0)//如果孩子
            {
                如果(ⅰ!= 0)//如果有一个previous命令
                {
                    dup2(old_fd [0],0); // old_pipe设置输入到子
                    关闭(old_fd [0]);
                    关闭(old_fd [1]);                }
                如果第(i + 1&下; num_commands_p)//如果存在下一CMD
                {
                    关闭(new_fd [0]); //设置new_pipe摆脱孩子的输出
                    dup2(new_fd [1],1);
                    关闭(new_fd [1]);                    ERR = execvp(commands_to_exec [Ⅰ] - GT; ARGS [0],commands_to_exec [Ⅰ] - GT;参数);
                    状态=走错了路。
                    退出(ERR);
                }
            }
            其他
            {
                waitpid函数(PID,和放大器;状态,0);
                如果(状态== -1)
                    出口(1);                如果(我!= 0)//如果有一个previous命令
                {
                    关闭(old_fd [0]);
                    关闭(old_fd [1]);
                }
                如果第(i + 1&下; num_commands_p)//如果有下一个CMD
                {
                    old_fd [0] = new_fd [0];
                    old_fd [1] = new_fd [1];
                }
                出口(0);
            } // 万一
        } //结束了        如果(ⅰ)//如果有一个的多个命令
        {
            关闭(old_fd [0]);
            关闭(old_fd [1]);
        }
    }
    否则//在父过程中,我们都在等着孩子来处理多个管道
        waitpid函数(CPID,和放大器;状态,0);
}

功能 execvp()需要结构数组。香港专业教育学院检查了我所有的解析部分,它工作正常。这是 execute_multiple_commands(),我遇到的麻烦的功能。

下面是code的结构:

  //名称:命令
//说明:保存一个命令(这意味着它可以是
//在命令不止一个令牌)
//LS -La将是一个命令的一个例子
//持有令牌的NUM命令阵列
结构命令
{
    烧焦** ARGS;
    INT num_args;
};


解决方案

我提出一个新的战略,R2:

 函数来完成(命令)
    如果命令是大小为1
        EXEC命令[0] ||死
    拆分命令到C1(第一个命令),C2(他们的休息)
    打开
    如果叉
        管道关闭输入端
        管DUP输出到标准输入
        做(C2)||死
    管道关闭输出端
    管DUP输入到stdout
    EXEC C1 ||死

使用递归函数,特别是如果你保持一个列表,可以帮助您简化逻辑。你不会真的要在这里不用担心堆栈深度,因为你的整个地址空间将被覆盖,反正

在其他消息方面,从手册页


  

从这些系统调用的一个,老人和成功返回后,
  新文件描述符可以互换使用。它们指的是
  同样的打开文件说明(请参阅open(2)),因此共享文件偏移
  和文件状态标志;例如,如果该文件偏移量是通过修改
  在描述符中的一个使用了lseek(2),偏移也被改变
  对于其他


当你说你关闭管道两端meanse?你真的是关闭它 - 它和输入/输出,你的程序是使用打算的标准

- >晚得多编辑< -

由于乔纳森·莱弗勒指出,上述信息是正确的。我曾与下面的程序证实了它:

 的#include<&unistd.h中GT;诠释主(){
    dup2(0,7);
    写(7,嘿,1 \\ n,7);
    关闭(0);
    写(7,嘿,2 \\ n,7);
    关闭(7);
    写(7,嘿,3 \\ n,7);
}

这会导致下面的输出:

  $ GCC dup2Test.c&放大器;&安培; ./a.out
嘿,1
嘿,2

谢谢,乔纳森!

I need to implement my shell that handles multiple pipe commands. For example I need to be able to handle this: ls | grep -i cs340 | sort | uniq | cut -c 5. I am assuming the problem is that I am not passing output of the previous command to the input of the next command. When I execute my code, it gives me no output. I am using this pseudo code:

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])

Here is the source code of the function that handles multiple pipes.

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);
}

Function execvp() takes array of structures. Ive checked all my parsing part, and it works fine. It is the execute_multiple_commands() function that I am having trouble with.

Here is the code for struct:

// 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;
};

解决方案

I suggest a new strategy, 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

Using a recursive function, especially if you're maintaining a list, will help you simplify your logic. You don't really have to worry about stack depth here, as your whole address space will be overwritten anyway.

In other news, from the man page:

After a successful return from one of these system calls, the old and new file descriptors may be used interchangeably. They refer to the same open file description (see open(2)) and thus share file offset and file status flags; for example, if the file offset is modified by using lseek(2) on one of the descriptors, the offset is also changed for the other.

Which meanse when you say you're closing both ends of the pipe? You really are closing it - it AND the standard in/out that your program is intending on using.

--> MUCH LATER EDIT <--

As Jonathan Leffler pointed out, the above information is in correct. I have confirmed it with the following program:

#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);
}

Which results in the following output:

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

Thanks, Jonathan!

这篇关于多重管道实现使用系统调用fork()的execvp()等待()管道() - 它根本不工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆