#c #shell #fork #signals #process-management
#c #оболочка #форк #сигналы #управление процессами
Вопрос:
В настоящее время я нахожусь в процессе создания небольшой оболочки в C .
Пользователь может ввести задание в командной строке, например exe1 amp;amp; exe2 amp;
. Аналогично оболочке BASH, я выполню только, exe2
если exe1
завершится успешно. Кроме того, все задание должно выполняться в фоновом режиме (как указано конечным amp;
оператором).
Прямо сейчас у меня есть jobManager
который обрабатывает выполнение заданий и job
структура, которая содержит исполняемый файл задания и их отдельные аргументы / условия. Задание запускается вызовом, fork()
а затем вызывается execvp()
с соответствующими аргументами. Когда задание заканчивается, у меня есть обработчик сигналов для SIGCHLD
, в котором я выполняю wait()
, чтобы определить, какой процесс только что завершился. Когда exe1
завершается, я просматриваю ее код выхода и принимаю решение о том, следует ли мне приступить к запуску exe2
.
Меня беспокоит, как мне запустить exe2
. Я обеспокоен тем, что если я использую свою функцию запуска JobManager из контекста моего SIGCHLD
обработчика, у меня может получиться слишком много SIGCHLD
функций-обработчиков, зависающих в стеке (например, если было 10 условных исполнений). Кроме того, просто не кажется хорошей идеей запускать следующее выполнение из обработчика сигнала, даже если это происходит косвенно. (Я пытался сделать что-то подобное 1,5 года назад, когда я только изучал обработку сигналов — кажется, я припоминаю, что у меня это не получилось).
Все вышеперечисленное должно выполняться в фоновом режиме, и я хочу избежать jobManager
сидения в напряженном ожидании, просто ожидая exe1
возвращения. Я бы также предпочел, чтобы отдельный поток не сидел без дела, просто ожидая начала выполнения другого процесса. Однако указание моему jobManager
начать выполнение следующего процесса из SIGCHLD
обработчика кажется плохим кодом.
Любая обратная связь приветствуется.
Ответ №1:
Я вижу два способа:
1) Заменить ваш sighandler циклом, который вызывает «sigwait» (см. man 3 sigwait)
затем в цикле
2) перед запуском создайте канал, и в mainloop вашей программы используйте «select» в дескрипторе канала, чтобы дождаться событий. В обработчике сигналов выполняется запись в канал, а в mainloop обрабатывается ситуация.
Комментарии:
1. Ах, ваше решение select () мне очень интересно. Я использовал select () / poll () в программировании сокетов и надеялся сделать что-то подобное здесь, чтобы избежать напряженного ожидания. Однако я предполагаю, что канал должен быть «глобальным», поскольку я не могу ничего предоставить обработчику сигналов во время выполнения.
2. циклы sigwait уродливы и здесь не приносят большой пользы, потому что они должны быть фоновыми заданиями.
3. Вы можете справиться с этим без глобальных переменных, но специфичным для Linux способом: signalfd создаст дескриптор для вас, чтобы получить информацию о сигналах.
4. Да — подробнее изучил signalfd — вы были правы. Спасибо за информацию.
Ответ №2:
Хммм, это хорошая задача.
Как насчет разветвления дважды, по одному разу для каждого процесса? Первая выполняется, а вторая останавливается. В родительском обработчике SIGCHLD отправьте SIGCONT второму дочернему, если это уместно, который затем отключается и запускает задание. Естественно, вы ОТМЕНЯЕТЕ вторую задачу, если первая не должна выполняться, что должно быть безопасно, потому что вы на самом деле ничего не настроили.
Как это звучит? У вас будет процесс, который ничего не делает, но это не должно длиться очень долго.
Комментарии:
1. Единственная причина, по которой я хотел бы избежать этого решения, заключается в том, что с моей точки зрения это кажется неудачной тратой ресурсов. Существуют также более сложные ситуации, которые я хочу иметь возможность обрабатывать, такие как циклы for () внутри задания.
2. @BSchlinker На самом деле это не пустая трата ресурсов, если только ваши дочерние процессы не делают много. fork () — это копирование при записи, поэтому, возможно, ожидающий процесс использует КБ или около того — и нет процессорного времени, поскольку оно было бы заблокировано.