Выполнение новой задачи на основе sigchld() из предыдущей задачи

В настоящее время я занимаюсь созданием небольшой оболочки в C++.

Пользователь может ввести задание по приглашению, например exe1 && exe2 &. Подобно оболочке BASH, я выполню exe2 только в том случае, если exe1 завершится успешно. Кроме того, вся работа должна выполняться в фоновом режиме (как указано оператором & в конце).

Прямо сейчас у меня есть jobManager, который обрабатывает выполнение заданий, и структура job, которая содержит исполняемый файл задания и его отдельные аргументы/условия. Задание запускается вызовом fork(), а затем вызовом execvp() с соответствующими аргументами. Когда задание завершается, у меня есть обработчик сигнала для SIGCHLD, в котором я выполняю wait(), чтобы определить, какой процесс только что завершился. Когда exe1 заканчивается, я наблюдаю за его кодом выхода и определяю, следует ли мне приступить к запуску exe2.

Меня беспокоит, как мне запустить exe2. Меня беспокоит, что если я использую мою функцию запуска jobManager из контекста моего обработчика SIGCHLD, я могу получить слишком много функций обработчика SIGCHLD, висящих в стеке (например, если было 10 условных исполнений). Кроме того, просто не кажется хорошей идеей начинать следующее выполнение с обработчика сигнала, даже если это происходит косвенно. (Я пытался сделать что-то подобное 1,5 года назад, когда только изучал обработку сигналов - кажется, я припоминаю, что у меня это не получилось).

Все вышеперечисленное должно происходить в фоновом режиме, и я не хочу, чтобы jobManager сидел в напряженном ожидании, просто ожидая возвращения exe1. Я также предпочел бы, чтобы отдельный поток не сидел без дела, просто ожидая начала выполнения другого процесса. Однако указание моему jobManager начать выполнение следующего процесса из обработчика SIGCHLD кажется плохим кодом.

Любая обратная связь приветствуется.


person BSchlinker    schedule 13.11.2011    source источник


Ответы (2)


Я вижу два пути:
1) Замените ваш sighandler циклом, который вызывает "sigwait" (см. man 3 sigwait)
затем в цикле

2) перед запуском создайте канал, а в основном цикле вашей программы используйте «выбрать» на дескрипторе канала для ожидания событий. В обработчике сигнала запишите в канал, а в mainloop обработайте ситуацию.

person fghj    schedule 13.11.2011
comment
Ах, ваше решение select() мне очень интересно. Я использовал select()/poll() в программировании сокетов и надеялся сделать что-то подобное здесь, чтобы избежать напряженного ожидания. Однако я предполагаю, что канал должен быть «глобальным», поскольку я не могу ничего предоставить обработчику сигналов во время выполнения. - person BSchlinker; 13.11.2011
comment
Циклы sigwait уродливы и не приносят здесь особой пользы, потому что они должны выполняться в фоновом режиме. - person Robert; 13.11.2011
comment
Вы можете справиться с этим без глобальных переменных, но особым образом для Linux: signalfd создает дескриптор для получения информации о сигналах. - person fghj; 13.11.2011
comment
Да, изучил signalfd подробнее, вы были правы. Спасибо за информацию. - person BSchlinker; 22.11.2011

Хм, это хорошо.

Как насчет двойного разветвления, по одному разу на процесс? Первый бежит, а второй останавливается. В родительском обработчике SIGCHLD отправьте сигнал SIGCONT второму потомку, если это необходимо, который затем сработает и запустит задание. Естественно, вы SIGKILL второй, если первый не должен запускаться, что должно быть безопасно, потому что вы на самом деле ничего не настроили.

Как это звучит? У вас будет процесс, который ничего не делает, но это не должно быть очень долго.

person Robert    schedule 13.11.2011
comment
Единственная причина, по которой я хотел бы избежать этого решения, заключается в том, что с моей точки зрения это кажется неудачной тратой ресурсов. Есть также более сложные ситуации, которые я хочу иметь возможность обрабатывать, например, циклы for() в задании. - person BSchlinker; 13.11.2011
comment
@BSchlinker На самом деле это не пустая трата ресурсов, если только ваши дочерние процессы много не делают. fork() является копированием при записи, поэтому, возможно, ожидающий процесс использует КБ или около того - и не требует процессорного времени, поскольку он будет заблокирован. - person Robert; 13.11.2011