Межпроцессное взаимодействие, чтение из нескольких дочерних элементов stdout

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

Что я пробовал до сих пор, так это то, что в моем приложении оболочки у меня есть две функции для одновременного запуска команд, execute() принимает несколько команд, и для каждой из команд это fork() дочерний процесс для выполнения команды, subprocess() берет 1 cmd и выполняет его.

void execute(std::vector<std::string> cmds) {
    int fds[2];
    pipe(fds);
    std::pair<pid_t, int> sp;

    for (int i = 0; i < cmds.size(); i++) {
        std::pair<pid_t, int> sp = this->subprocess(cmds[i], fds);
    }

    
    // wait for all children
    while (wait(NULL) > 0);
    close(sp.second);
}

std::pair<pid_t, int> subprocess(std::string &cmd, int *fds) {
    std::pair<pid_t, int> process = std::make_pair(fork(), fds[0]);
    if (process.first == 0) {
        close(fds[0]); // no reading
        dup2(fds[1], STDIN_FILENO);
        close(fds[1]);
        char *argv[] = {"/bin/sh", "-c", cmd.data(), NULL};
        execvp(argv[0], argv);
        exit(0);
    }
    close(fds[1]); // only reading
    return process;
} 

Проблема здесь в том, что когда я выполняю несколько команд в своей пользовательской оболочке (здесь я не углубляюсь в подробности, но в какой-то момент она вызовет execute()). каков захваченный вывод, например

если входные команды

echo im done, yet?
echo nope
echo maybe

затем, при записи в случае STDIN_FILENO, вывод выглядит так (где STDIN_FILENO) — это мой маркер для пользовательского ввода.

im done, yet?
nope
maybe
>>> nope
maybe
im done, yet?
>>> im done, yet?
nope
maybe

в письме к >>> случае кажется, что он игнорирует одну из команд (вероятно, первую дочернюю), я не уверен, почему?

maybe
nope
>>> maybe
nope
>>> nope
maybe
>>> maybe
nope
>>> nope

Итак, потенциальные вещи, которые, как я думал, находятся в моей оболочке, которую я использую STDOUT_FILENO для пользовательского ввода в цикле while ofc, это может каким-то образом конфликтовать со стандартным регистром. С другой стороны, в основном процессе (родительском) я жду выхода всех детей, поэтому дети почему-то не выходят, но ребенок должен умереть после execvp, верно? Более того, я закрываю конец чтения в основном процессе std::cin >> .... На данный момент я не уверен, почему этот случай происходит?

Не следует ли мне использовать close(sp.second) для такого процесса? Если я использую временный файл для перенаправления stdout дочернего процесса, все будет хорошо? и если да, то не могли бы вы объяснить, почему?

Получение данных из формы с помощью JavaScript - краткое руководство
Получение данных из формы с помощью JavaScript - краткое руководство
Получить данные из формы с помощью JS очень просто: вы запрашиваете элемент формы, передаете его конструктору new FormData() и, наконец, получаете...
Пользовательские правила валидации в Laravel
Пользовательские правила валидации в Laravel
Если вы хотите создать свое собственное правило валидации, Laravel предоставляет возможность сделать это. Создайте правило с помощью следующей...
3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
1
0
23
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В показанном коде есть несколько фундаментальных концептуальных проблем.

std::pair<pid_t, int> sp;

Это объявляет новый объект std::pair. Все идет нормально.

std::pair<pid_t, int> sp = this->subprocess(cmds[i], fds);

Это объявляет новый объект std::pair внутри цикла for. Просто у него такое же имя, как у объекта sp в области видимости функции. Но это другой объект, который не имеет к этому никакого отношения. Вот как работает C++: когда вы объявляете объект внутри внутренней области видимости, внутри оператора if, цикла for или чего-то еще, что находится внутри другой пары { ... }, вы в конечном итоге объявляете новый объект. Неважно, совпадает ли его имя с другим именем, объявленным в большей области. Это новый объект.

    // wait for all children
    while (wait(NULL) > 0);
    close(sp.second);

Здесь есть две отдельные проблемы.

  1. Для начала, если мы обратили внимание: этот sp объект ничем не инициализирован.

  2. Если целью здесь является чтение из дочерних процессов, эта часть полностью отсутствует, и это следует делать до, ожидая завершения дочерних процессов. Если, как описано здесь, дочерние процессы будут писать в этот канал, то канал должен быть прочитан. В противном случае, если из канала ничего не читается: внутренний буфер канала ограничен, и если дочерние процессы заполняют канал, они будут заблокированы, ожидая, пока канал будет прочитан. Но родительский процесс ждет существования дочерних процессов, поэтому все будет зависать.

Наконец, также неясно, почему дескриптор файла канала передается той же функции только для того, чтобы вернуть std::pair с тем же дескриптором файла. std::pair не служит никакой полезной цели в показанном коде, поэтому вполне вероятно, что есть еще код, который здесь не показан, где он используется.

По крайней мере, все вышеперечисленные проблемы должны быть исправлены, чтобы показанный код работал правильно. Если есть другой код, который не показан, он также может иметь или не иметь дополнительные проблемы.

ну очень опрометчиво с моей стороны, спасибо, что указали, я как-то по ошибке передекларирую sp, пропустил мимо ушей.

null 10.04.2022 19:08

Другие вопросы по теме