Проблемы с трубой

0

Я пытаюсь создать простую оболочку, но я не могу заставить эту функцию работать. Я не могу найти в этом ничего плохого. Я тестирую простую команду, например "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:

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

Как я уже упоминал в описании, я создаю простую оболочку, которая может обрабатывать несколько каналов и, в будущем, перенаправлять.

Было бы весьма признательно мнение кого-то другого.

  • 1
    Что она делает? И если вы думаете, что определение Command будет полезным, вы будете правы.
  • 0
    Вы точно не описали, как вы ожидаете, что ваша программа будет работать, но важно понимать, что если exec успешно запустит новую программу в дочернем процессе, исходный код этого дочернего процесса будет полностью заменен новой программой. Поэтому у него нет возможности запустить другую программу, что должно быть сделано родителем. Но у вас есть родитель, ожидающий. Таким образом, кажется, что только первая программа действительно запущена. Ваша обработка труб тоже кажется слишком сложной.
Показать ещё 1 комментарий
Теги:
pipe
fork

1 ответ

0

Ваши проблемы:

  1. Вы ждете завершения ls прежде чем запускать sort.
  2. Вы закрываете все каналы в родительском элементе перед запуском sort.

В цикле for вы обрабатываете только дочерние элементы; родительский элемент должен возвратиться назад и запустить следующего дочернего элемента.

После цикла for вам нужно закрыть каналы в родительском. Затем вам понадобится цикл для сбора трупов обоих дочерних процессов (отмечая, что если оболочка ранее запускала некоторые процессы, выполняющиеся в фоновом режиме, может существовать несколько трупов, отличных от тех, которые находятся в текущем конвейере до завершения текущего конвейера).

Также обратите внимание, что правильное место для сообщения об ошибках - 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--;
            }
        }
    }
}

Вероятно, лучший способ обработать цикл wait() в C++ с помощью vector<pid_t> kids и подходящих алгоритмов STL, применяемых к нему, но это упражнение для вас. Показанный код имеет предположение о том, что в нем есть два дочерних процесса, в двух точках (отмечены); вам нужно будет отсортировать их для более общего случая N процессов в конвейере, и этот вектор также должен помочь этому.

Обратите внимание, что если исключение выбрасывается, трубы не закрываются. Это утечка ресурсов. Кроме того, если первый процесс запущен, но второй вилок выходит из строя, у вас есть процесс, который (вероятно) не умрет, потому что его входные каналы все еще открыты в родительском процессе. Прочная программа должна будет исправить это.


Предупреждение: код не был передан через компилятор;там могут быть проблемы.


О, Футц! Когда в конвейере есть две команды (как в "ls | sort"), требуется только один канал. Я был втянут в код в вопросе!

Я очистил большую часть мусора, который не нужен для случая "ls | sort", как в функции struct Command и в функции execute(). Я получил базовый 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
  • 0
    Джонатан, прости, я новичок в такого рода вещах. Можете ли вы пойти более подробно или определить мои ошибки программно?
  • 0
    Ну, вы могли бы потратить немного времени, чтобы посмотреть и подумать об этом ... но я скоро обновлю свой ответ.
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню