Почему «su -c <команда> &», по-видимому, позволяет команде работать в фоновом режиме, не зависая

11

Я помогал коллеге, у которого были проблемы с фоновым процессом, периодически умирая.

Я обнаружил, что они запускают фоновый процесс, войдя на сервер и выполнив:

su - <user> -c '<command>' &

«Ага», воскликнул я. «Если вы начнете команду с« & », она будет зависать при выходе из управляющего терминала. Для этого вам нужно использовать что-то вроде nohup. Действительно, этот процесс должен поддерживать работу в качестве демона, а не в тот момент».

Мы протестировали приведенную выше команду, чтобы продемонстрировать мою точку зрения, и ... она работала: процесс, запущенный командой , не завершился, когда мы вышли из терминала, который выполнил указанную выше команду.

команда - это пользовательский скрипт Python, чей вывод идет в файл. Насколько я могу судить, в скрипте нет интеллектуальных возможностей, подобных демонам. Он не выполняет никаких действий, необходимых для запуска в качестве демона, перечисленных на странице Википедии: Демон (вычисления): Создание .

Запуск команды выглядит так, как ожидается:

<command> &
exit

В вышеприведенном случае фоновый процесс, запущенный командой, завершается, когда мы выходим из терминала.

У меня вопрос такой:

  1. Что происходит, когда мы добавляем «su - -c &», который предотвращает выход процесса при выходе из нашего терминала. Я хотел бы понять в деталях относительно терминала управления, стандартного ввода и вывода и т. Д.

  2. Это разумный способ для достижения цели запуска этой команды в качестве фонового процесса. Если нет, то почему?

Я хочу распространять лучшие практики в своей компании, но я должен быть в состоянии продемонстрировать и поддержать любые мои рекомендации.

Я тоже хочу понять, что именно происходит.

Сэм Элстоб
источник

Ответы:

12

Есть несколько способов остановить процесс из-за умирающего терминала.

  1. Первый способ заключается в том , что драйвер терминала в ядре посылает сигнал SIGHUP в процесс контрольного , для которого терминал является контролирующим терминалом . В большинстве случаев процесс управления - это оболочка, которая изначально запускается в терминале, а его управляющий терминал - это тот, к которому подключены его stdin, stdout и stderr. Процесс становится отключенным от управляющего терминала, если он вызывает setsid.

  2. Второй способ заключается в том, что оболочка, когда она получает сигнал SIGHUP, повторно отправляет этот сигнал своим подпроцессам, точнее - фоновым заданиям. Некоторые оболочки (ksh, bash, zsh) имеют disownвстроенную функцию, позволяющую оболочке не отправлять SIGHUP на определенную работу.

  3. Если терминал отключается и программа пытается прочитать с него, он уведомляется о состоянии конца файла (или, возможно, EIO для фонового задания). Если терминал отключается и программа пытается выполнить запись в него, запись возвращается с ошибкой EIO. Это может привести к закрытию программы.

Итак, что suздесь меняется, так это то, что (2) обычно заставляет начальную оболочку уничтожать фоновое задание, но поскольку этот фоновый процесс выполняется от имени другого пользователя, сигнал не может быть доставлен, и фоновый процесс продолжает работать.

Жиль "ТАК - прекрати быть злым"
источник
Я видел, как пользователь root chroot --userspec root:root / sh -c "exec some_forever_process" &. Задание выполняется от имени того же пользователя, без явного nohupдо или disownпосле. Так что для этого случая, как это сигнал не может быть доставлен на срок выхода?
Тоддиус Жо
@ToddiusZho Presumable some_forever_processпредназначался для запуска навсегда (демон) и поэтому защищает себя от получения SIGHUP с выхода терминала (запуская в своей собственной группе процессов).
Жиль "ТАК - перестань быть злым"
bash / tail не защищает от SIGHUP, но это все еще работает: `` `local $ ssh root @ REMOTE_HOST remote # chroot --userspec root: root / sh -c" exec bash -c 'tail -f / dev / null '"& [1] 6376 remote # ps -p $! --no-heading -o pid, uid, команда -ww 6376 0 tail -f / dev / null remote # выйти из локального $ ssh root @ REMOTE_HOST remote # ps -p 6376 --no-heading -o pid, uid, команда -ww 6376 0 tail -f / dev / null `` `
Тоддиус Жо