Межпроцессное взаимодействие, чтение из нескольких дочерних элементов 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 дочернего процесса, все будет хорошо? и если да, то не могли бы вы объяснить, почему?

Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
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

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

Многопроцессорность и многопоточность — теоретическое уточнение
Есть ли причина этой разницы между потоками и процессами Python?
Невозможно записать/прочитать значение в/из зарегистрированного общего объекта с многопроцессорным сервером BaseManager в нескольких процессах
Как добавить множество диапазонов подмножества в новый фрейм данных после некоторой фильтрации?
Как избежать блокировки цикла событий asyncio с помощью циклических функций
Многопроцессорность Python, как вернуть переменные для процесса, который можно отменить
Задача, связанная с ЦП — производительность многопроцессорного подхода намного хуже, чем у синхронного подхода — почему?
Как работает after() и многопроцессорность с использованием tkinter
Использование concurrent.futures с бесконечным итератором и критериями остановки
Многопроцессорные вызовы Python становятся медленнее для более поздних вызовов