Есть ли вариант UNIX, в котором дочерний процесс умирает вместе со своим родителем?

41

Я уже довольно давно изучаю поведение ядра Linux, и мне всегда было ясно, что:

Когда процесс умирает, все его дочерние элементы возвращаются initпроцессу (PID 1) до тех пор, пока они в конечном итоге не умрут.

Однако недавно кто-то с большим опытом работы с ядром сказал мне, что:

Когда процесс завершается, все его дочерние элементы также умирают (если вы не используете, NOHUPв этом случае они возвращаются init).

Теперь, хотя я не верю в это, я все же написал простую программу, чтобы убедиться в этом. Я знаю, что не следует полагаться на time ( sleep) для тестов, поскольку все зависит от планирования процессов, но для этого простого случая, я думаю, этого достаточно.

int main(void){
    printf("Father process spawned (%d).\n", getpid());
    sleep(5);

    if(fork() == 0){
        printf("Child process spawned (%d => %d).\n", getppid(), getpid());
        sleep(15);
        printf("Child process exiting (%d => %d).\n", getppid(), getpid());
        exit(0);
    }

    sleep(5);
    printf(stdout, "Father process exiting (%d).\n", getpid());
    return EXIT_SUCCESS;
}

Вот вывод программы, с ассоциированным psрезультатом каждый раз, когда printfговорит:

$ ./test &
Father process spawned (435).

$ ps -ef | grep test
myuser    435    392   tty1    ./test

Child process spawned (435 => 436).

$ ps -ef | grep test
myuser    435    392   tty1    ./test
myuser    436    435   tty1    ./test

Father process exiting (435).

$ ps -ef | grep test
myuser    436    1     tty1    ./test

Child process exiting (436).

Теперь, как вы можете видеть, это ведет себя так, как я и ожидал. Сиротский процесс (436) возвращается к init(1), пока он не умрет.

Однако существует ли система на основе UNIX, к которой это поведение не применяется по умолчанию? Существует ли какая-либо система, в которой смерть процесса немедленно вызывает смерть всех его детей?

Джон У. Смит
источник

Ответы:

68

Когда процесс завершается, все его дочерние элементы также умирают (если вы не используете NOHUP, в этом случае они возвращаются к init).

Это не правильно. Смертельно неправильно. Человек, говорящий, что либо ошибся, либо перепутал конкретную ситуацию с общим делом.

Есть два способа, которыми смерть процесса может косвенно вызвать смерть его детей. Они связаны с тем, что происходит, когда терминал закрыт. Когда терминал исчезает (исторически , потому что последовательная линия была вырезана из - за модемом зависание, в настоящее время обычно потому , что пользователь закрыл окно терминала эмулятора), сигнал SIGHUP посылаются на контрольном процесс работает в том , что терминал - как правило, начальная оболочка начала в этом терминале. Снаряды обычно реагируют на это, выходя. Перед выходом оболочки, предназначенные для интерактивного использования, отправляют HUP на каждое задание, которое они начали.

Запуск задания из оболочки с nohupперебоями во втором источнике сигналов HUP, потому что задание будет тогда игнорировать сигнал и, следовательно, ему не будет приказано умереть при исчезновении терминала. Другие способы прервать распространение сигналов HUP от оболочки до заданий включают использование disownвстроенной функции оболочки, если она есть (задание удалено из списка заданий оболочки), и двойное разветвление (оболочка запускает дочерний элемент, который запускает дочерний элемент). своего собственного и немедленно выходит; оболочка не знает своего внука).

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

Жиль "ТАК - перестань быть злым"
источник
1
Есть третий способ, что-то с контрольными группами. Все, что я знаю, это то, что systemd использует его для принудительного убийства.
11
5
@JanHudec Я думаю, ты путаешься nohupс disown. disownявляется встроенной в некоторую оболочку, которая удаляет запущенное задание из таблицы заданий (принимая аргумент, подобный этому %1), и основной полезный побочный эффект этого заключается в том, что оболочка не отправляет SIGHUP подпроцессу. nohupэто внешняя утилита, которая игнорирует SIGHUP (и делает несколько других вещей), а затем запускает команду, переданную в ее командной строке.
Жиль "ТАК ... перестать быть злым"
@Gilles: Нет, я не был, но, глядя на strace nohup, он игнорирует сигнал перед execкомандой.
Ян Худек
Спасибо за ваш ответ! Мне особенно понравилось немного исторического фона, связанного с зависаниями модема. Я начал беспокоиться, что все это время неправильно понимал ядро ​​...
Джон У. Смит,
3
Также следует отметить, что программа завершается, когда она получает SIGHUP, потому что обработчик сигнала по умолчанию для SIGHUP должен выйти. Но любая программа может реализовать свой собственный обработчик SIGHUP, который может привести к тому, что он не завершится, когда родительская оболочка отправит ему SIGHUP.
Slebetman
12

Когда процесс завершается, все его дочерние элементы также умирают (если вы не используете NOHUP, в этом случае они возвращаются к init).

Это правильно, если процесс является лидером сеанса. Когда лидер сеанса умирает, SIGHUP отправляется всем участникам этого сеанса. На практике это означает, что его дети и их потомки.

Процесс делает себя лидером сессии, вызывая setsid. Снаряды используют это.

Деннис
источник
Я только что проверил тест по этому поводу, но я не смог воспроизвести поведение, которое вы описываете ... Я породил процесс, дал ему новый сеанс ( fork, уничтожить родительский процесс setsid) и разбудил другого ребенка в этот новый сеанс. Однако, когда родительский процесс (новый лидер сеанса) умер, ребенок не получил SIGHUP и привязывался к нему, initпока в конце концов не завершился.
Джон У. С. Смит,
Из setpgid(2)man-страницы: Если у сеанса есть управляющий терминал, и флаг CLOCAL для этого терминала не установлен, и происходит зависание терминала, то лидеру сеанса отправляется SIGHUP. Если лидер сеанса выходит, то сигнал SIGHUP также будет отправляться каждому процессу в группе процессов переднего плана управляющего терминала.
ниндзя
1
@ninjalj Тогда я предполагаю, что этот ответ действителен тогда и только тогда, когда лидер сеанса также является управляющим процессом терминала. Стандартный лидер сеанса, независимый от любого терминала, не убьет своих детей. Это правильно?
Джон WH Smith
-1

Таким образом, вышеприведенные плакаты говорят о том, что дети не умирают, родитель убивает их (или посылает им сигнал, по которому они заканчивают). Таким образом, вы можете получить то, о чем просите, если вы запрограммируете Родителя (1) вести учет всех его дочерних элементов и (2) отправлять сигнал всем его дочерним элементам.

Это то, что делает Shell, и это должно быть тем, что делает ваш родительский процесс. Может потребоваться перехватить сигнал HUP в родительском элементе, чтобы у вас все еще было достаточно контроля, чтобы убить дочерних элементов.

марко
источник
Многие причины могут заставить родителя умереть. Например, невозможно предотвратить выживание детей, если их родители SIGKILLed.
Эмиль Йержабек поддерживает Монику
-1

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

Это действительно может привести к смерти процессов. Фактически, это стандартный способ yesсмерти программы .

Если я выполню

(yes;echo $? >&2)|head -10

в моей системе ответ

y
y
y
y
y
y
y
y
y
y
141

и 141 действительно 128 + SIGPIPE:

   SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                 readers

от man 7 signal.

user86542
источник
2
Но процесс, читающий из канала, не обязательно (или даже обычно) его родитель. Так что это действительно очень отличается от того, о чем вопрос.
Бармар