Как привязать дочерний процесс к новому родителю в BASH
Немного о задаче: Работаем в Linux (Astra, OSнова). Есть оболочка. Из-под неё запускается программа, для которой та оболочка -- родитель. Сорс оболочки закрыт, неизвестен, и менять его нельзя. Внутри этой программы вызывается bash-скрипт, для которого уже сама программа -- родитель. В скрипте принимается решение, о том, что программу необходимо перезапустить, но если сделать это в тупую (убить процесс и создать новый), то исходная оболочка теряет из прямого вида программу, которую запустила, и дальнейшая её работа не является прогнозируемой. Перезапускать оболочку -- возможное, но не желательное решение (на котором я остановлюсь, если не найду чего-то лучшего). Оболочка нужна, в т.ч., и для обработки результата работы программы, поэтому запустить программу вообще отдельно -- тоже не лучший вариант.
Возникает вопрос: существует ли способ привязать порождаемый или уже работающий процесс к иному родителю? Есть ли решение для bash-а, или лучше пытаться что-то сделать в Си?
Ответы (3 шт):
архитектуру приложения стройте так:
из оболочки запускаете программу.
программа делает пару pty/pts.
дальше делается двойной fork и запускается супервизор(программа перезагружалка)
супервизор запускает основную часть кода на pty
супервизор записывает номер pts в файлик
нулевой форк соединяет вывод pts на stdout/stdin
при отвале оболочки - программа остаётся в фоне.
при перезапуске смотрите сохраненные pts и если она жива то соединяете с ней.
Нет простых способов запустить процесс с чужим родительским PPID или изменить его в процессе работы, ни командами bash, ни API POSIX/Linux.
В скрипте принимается решение, о том, что программу необходимо перезапустить, но если сделать это в тупую (убить процесс и создать новый), то исходная оболочка теряет из прямого вида программу, которую запустила, и дальнейшая её работа не является прогнозируемой.
Однако, если разделить вашу программу на основной процесс и процесс монитора, к примеру, с помощью fork(), то не потребуется изменять ppid.
Процесс монитора должен выполнять в цикле примерно следующее:
- Запуск основного процесса;
- Ожидание основного процесса;
- Если основной процесс завершился заданным сигналом, скажем SIGUSR1, то переход к п. 1;
- Завершение монитора с тем же кодом возврата, что и основной процесс (при необходимости, можно даже самоубиться тем же самым сигналом, что и основной процесс, что б не нарушать отчётность).
Тогда ваш bash скрипт сможет, в нужный момент, просто выполнить kill -USR1 {№ основного процесса} без каких-либо проблем.
P.S.
В принципе, "для надёжности и украшательства" можно создать трубу от основного процесса к монитору. Монитор её не читает, только закрывает при рестарте или завершении, а отдельный служебный поток основного процесса пишет (недолго, пока не зависнет на ожидании). В такой конструкции, в случае внезапной безвременной кончины монитора (по ошибке или по инициативе "оболочки") основной процесс получит SIGPIPE и тоже завершится.
Системный вызов exec запускает новый процесс на месте текущего, того который его вызвал. При этом родительский процесс не замечает что в его ребёнке что-то изменилось. То есть вы можете сколько угодно раз запускать новые задачи в рамках одного и того же дочернего процесса.
The
exec()family of functions replaces the current process image with a new process image.
man bash, секция SHELL BUILTIN COMMANDS, команда exec:
If command is specified, it replaces the shell. No new process is created.