管道问题

时间:2014-02-11 01:40:50

标签: c++ shell fork pipe

我正在尝试创建一个简单的shell,但我似乎无法使这个功能起作用。我发现它没有任何问题。我正在测试一个简单的命令,比如“ls | sort”,但我根本没有输出。

void execute(std::vector<Command *> cmds)
{
    int inp[2], out[2];
    pipe(inp);
    pipe(out);
    int status, fd = 0;
    bool switcher = true;

    for (auto i = 0; i < cmds.size(); i++)
    {
        auto pid = fork();

        if (pid == -1) {

            throw std::runtime_error("Could not fork.");

        } else if (pid == 0) {

            if (i == 0) {
                dup2(inp[1], 1);
            } else if (i == cmds.size() - 1) {
                dup2(out[0], 0);
            } else {
                if (switcher) {
                    dup2(inp[0], 0);
                    dup2(out[1], 1);
                    switcher = false;
                } else {
                    dup2(inp[1], 1);
                    dup2(out[0], 0);
                    switcher = true;
                }
            }

            close(inp[0]);
            close(inp[1]);
            close(out[0]);
            close(out[1]);

            if(execvp(cmds[i]->args_char[0], cmds[i]->args_char.data()) < 0) {
                std::cout << "Command not found." << std::endl;
                exit(1);
            }

        } else {

            close(inp[0]);
            close(inp[1]);
            close(out[0]);
            close(out[1]);

            wait(&status);
        }
    }
}

这是Command结构:

struct Command {
    int identity;
    std::vector<char *> args_char;
    std::vector<std::string> args_string;
    bool redirectin;
    bool redirectout;
    std::string filename;

    Command(int id, std::vector<char *> cmds_char, std::vector<std::string> cmds_string)
    {
        identity = id;
        args_string = cmds_string;
        args_char = cmds_char;
        redirectin = false;
        redirectout = false;

        for (auto i = 0; i < args_string.size(); i++) {
            if (args_string[i] == ">") {
                redirectout = true;
                filename = args_string.back();
                args_char.erase(args_char.end() - 3, args_char.end());
                break;
            }
            if (args_string[i] == "<") {
                redirectin = true;
                filename = args_string.back();
                args_char.erase(args_char.end() - 3, args_char.end());
                break;
            }
        }

        if (redirectin == true && redirectout == true)
            throw std::runtime_error("Invalid redirection.");
    }
};

正如我在描述中所提到的,我正在创建一个可以处理多个管道的简单shell,以及将来重定向。

非常感谢别人的意见。

1 个答案:

答案 0 :(得分:0)

你的问题是:

  1. 您正在等待ls完成,然后才能启动sort
  2. 在启动sort之前,您正在关闭父级中的所有管道。
  3. for循环中,您只进行子处理;父母需要循环回来并启动下一个孩子。

    for循环之后,您需要关闭父级中的管道。然后你需要一个循环来收集两个子进程的尸体(注意如果shell先前启动了一些在后台运行的进程,那么在当前管道完成之前可能存在除当前管道之外的一些尸体)。

    另请注意,报告错误的正确位置为cerr,而不是cout

    void execute(std::vector<Command *> cmds)
    {
        int inp[2], out[2];  // Correct use of 2
        if (pipe(inp) != 0 || pipe(out) != 0)
            throw std::runtime_error("Failed to create pipes."); // may leak descriptors
        int status, fd = 0;
        bool switcher = true;
        pid_t pids[2];  // Inappropriate use of 2
    
        for (auto i = 0; i < cmds.size(); i++)
        {
            auto pid = fork();
    
            if (pid == -1)
                throw std::runtime_error("Could not fork.");
            else if (pid == 0)
            {
                if (i == 0)
                    dup2(inp[1], 1);
                else if (i == cmds.size() - 1)
                    dup2(out[0], 0);
                else if (switcher)
                {
                    dup2(inp[0], 0);
                    dup2(out[1], 1);
                    switcher = false;
                }
                else
                {
                    dup2(inp[1], 1);
                    dup2(out[0], 0);
                    switcher = true;
                }
    
                close(inp[0]);
                close(inp[1]);
                close(out[0]);
                close(out[1]);
    
                execvp(cmds[i]->args_char[0], cmds[i]->args_char.data();
                std::cerr << "Command not found." << std::endl;
                exit(1);
                }
            }
            else
                pids[i] = pid;
            //} else {
            //    close(inp[0]);
            //    close(inp[1]);
            //    close(out[0]);
            //    close(out[1]);
            //    wait(&status);
            }
        }
    
        // Children launched - wait for the kids to die (morbid business!)
        close(inp[0]);
        close(inp[1]);
        close(out[0]);
        close(out[1]);
    
        int corpse;
        int kids = 2;  // Inappropriate use of 2
        while (kids > 0 && (corpse = wait(&status)) > 0)
        {
            for (int i = 0; i < kids; i++)
            {
                if (corpse == pids[i])
                {
                    for (j = i+1; j < kids; j++)
                         pids[i++] = pids[j];
                    kids--;
                }
            }
        }
    }
    

    可能有更好的方法来处理C ++中的wait()循环,并使用vector<pid_t> kids和适用的STL算法,但这对你来说是一个练习。显示的代码假设“有两个子进程”在几个点(标记)硬连线;你需要为管道中N个过程的更一般情况对它们进行排序,并且向量也应该有所帮助。

    请注意,如果抛出异常,则不会关闭管道。这会泄漏资源。此外,如果第一个进程启动但第二个fork失败,那么您有一个(可能)不会死的进程,因为它的输入管道仍在父进程中打开。一个强大的程序需要解决这个问题。


    警告:代码尚未通过编译器传递;很容易出问题。


    哦,Futz!当管道中有两个命令时(如“ls | sort”),只需要一个管道。我被问题中的代码所吸引!

    我在struct Commandexecute()函数中清除了“ls | sort”案例中不需要的大部分垃圾。我派生了一个基本的main(),它对要执行的命令进行硬编码。这导致了SSCCE(Short, Self-Contained, Correct Example),如下所示:

    #include <vector>
    #include <iostream>
    #include <stdexcept>
    #include <unistd.h>
    
    struct Command
    {
        std::vector<char *> args_char;
    
        Command(std::vector<char *> cmds_char)
        {
            args_char = cmds_char;
        }
    };
    
    void execute(std::vector<Command *> cmds)
    {
        int inp[2];
        if (pipe(inp) != 0)
            throw std::runtime_error("Failed to create pipes.");
        pid_t pids[2];  // Inappropriate use of 2
    
        for (auto i = 0U; i < cmds.size(); i++)
        {
            auto pid = fork();
    
            if (pid == -1)
                throw std::runtime_error("Could not fork.");
            else if (pid == 0)
            {
                std::cerr << i << ": PID " << (int)getpid() << "\n";
                if (i == 0)
                {
                    std::cerr << i << ": DUP 1\n";
                    dup2(inp[1], 1);
                }
                else if (i == cmds.size() - 1)
                {
                    std::cerr << i << ": DUP 2\n";
                    dup2(inp[0], 0);
                }
    
                close(inp[0]);
                close(inp[1]);
    
                std::cerr << i << ": CMD " << cmds[i]->args_char[0] << "\n";
                execlp(cmds[i]->args_char[0], cmds[i]->args_char[0], (char *)0);
                std::cerr << "Command " << cmds[i]->args_char[0] << " not found." << std::endl;
                exit(1);
            }
            else
                pids[i] = pid;
        }
    
        // Children launched - wait for the kids to die (morbid business!)
        std::cerr << "Parent: waiting for the inevitable\n";
        close(inp[0]);
        close(inp[1]);
    
        int status;
        int corpse;
        int kids = 2;  // Inappropriate use of 2
        while (kids > 0 && (corpse = wait(&status)) > 0)
        {
            std::cerr << "2: KID " << corpse << " status " << status << "\n";
            for (int i = 0; i < kids; i++)
            {
                if (corpse == pids[i])
                {
                    for (int j = i + 1; j < kids; j++)
                        pids[i++] = pids[j];
                    kids--;
                }
            }
        }
        std::cerr << "2: DONE\n";
    }
    
    int main()
    {
        char prg0[] = "ls";
        char prg1[] = "sort";
        std::vector<char *> cmd0 = { prg0, 0 };
        std::vector<char *> cmd1 = { prg1, 0 };
        std::vector<Command *> cmds =
        {
            new Command(cmd0),
            new Command(cmd1),
        };
        try
        {
            execute(cmds);
        }
        catch (std::exception &bug)
        {
            std::cerr << "Exception: " << bug.what() << "thrown\n";
        }
        return 0;
    }
    

    说明性输出:

    Parent: waiting for the inevitable
    0: PID 36439
    0: DUP 1
    0: CMD ls
    1: PID 36440
    1: DUP 2
    1: CMD sort
    2: KID 36439 status 0
    cmds
    cmds.cpp
    cmds.dSYM
    makefile
    2: KID 36440 status 0
    2: DONE