Линукс убивает фоновые процессы, если мы закрываем терминал, с которого он запускался?

У меня есть встроенная система, на которой я выполняю telnet, а затем запускаю приложение в фоновом режиме:

./app_name &

Теперь, если я закрою терминал и сделаю telnet с другого терминала, и если я проверю, то вижу, что этот процесс все еще запущен.

Чтобы проверить это, я написал небольшую программу:

#include<stdio.h>
main()
{
    while(1);
}

Я запустил эту программу на своем локальном ПК в фоновом режиме, и я закрыл терминал.

Теперь, когда я проверил этот процесс с другого терминала, я обнаружил, что этот процесс также был убит.

Мой вопрос:

  • Почему поведение undefined для одного и того же типа процесса?
  • На чем это зависит?
  • Это зависит от версии Linux?

Ответы

Ответ 1

Кто должен убивать задания?

Обычно задания переднего и заднего плана убиваются SIGHUP, отправленные ядром или оболочкой в ​​разных обстоятельствах.


Когда ядро ​​отправляет SIGHUP?

Ядро отправляет SIGHUP в процесс управления:

  • для реального (аппаратного) терминала: при обнаружении разъединения в драйвере терминала, например. при зависании на модемной линии;
  • для pseudoterminal (pty): когда последний дескриптор, ссылающийся на основную сторону pty, закрыт, например. когда вы закрываете окно терминала.

Ядро отправляет SIGHUP в другие группы процессов:

  • в группу процессов переднего плана, когда процесс управления завершается;
  • to осиротевшая группа процессов, когда она становится сиротой и она остановила участников.

Процесс управления - это лидер сеанса, который установил соединение с управляющим терминалом.

Как правило, процесс управления - это ваша оболочка. Итак, подведем итог:

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

Обратите внимание, что ядро ​​не отправляет SIGHUP в фоновый процесс, если оно не содержит остановленных процессов.


Когда bash отправляет SIGHUP?

Bash отправляет SIGHUP ко всем заданиям (передним и задним):

  • когда он получает SIGHUP, и это интерактивная оболочка (а поддержка управления заданиями включена во время компиляции);
  • когда он завершается, это интерактивная оболочка для входа, и опция huponexit установлена ​​(и поддержка управления заданиями включена во время компиляции).

Подробнее здесь.

Примечания:

  • bash не отправляет SIGHUP в задания, удаленные из списка заданий, используя disown;
  • процессы начали использовать nohup игнорировать SIGHUP.

Подробнее здесь.


Как насчет других оболочек?

Обычно оболочки распространяют SIGHUP. Генерация SIGHUP при нормальном выходе менее распространена.


Telnet или SSH

В telnet или SSH при закрытии соединения (например, при закрытии окна telnet на ПК) должно произойти следующее:

  • клиент убит;
  • сервер обнаруживает закрытие клиентского соединения;
  • сервер закрывает основную сторону pty; Ядро
  • обнаруживает, что master pty закрыт и отправляет SIGHUP в bash;
  • bash получает SIGHUP, отправляет SIGHUP на все задания и завершает работу;
  • каждое задание получает SIGHUP и завершается.

Проблема

Я могу воспроизвести вашу проблему, используя bash и telnetd из busybox или dropbear SSH-сервера: иногда фоновое задание не получает SIGHUP (и не завершается) при закрытии клиентского соединения.

Кажется, что условие гонки происходит, когда сервер (telnetd или dropbear) закрывает главную сторону pty:

  • обычно bash получает SIGHUP и сразу же убивает фоновые задания (как и ожидалось) и завершает;
  • но иногда bash обнаруживает EOF на подчиненной стороне pty перед обработкой SIGHUP.

Когда bash обнаруживает EOF, он по умолчанию немедленно прекращается без отправки SIGHUP. И фоновое задание остается запущенным!


Решение

Можно настроить bash на отправку SIGHUP на обычный выход (включая EOF):

  • Убедитесь, что bash запущен как оболочка входа. huponexit работает только для систем входа в систему AFAIK.

    Локальная оболочка включена опцией -l или дефис в argv[0]. Вы можете настроить telnetd на запуск /bin/bash -l или лучше /bin/login, который вызывает /bin/sh в режиме оболочки входа.

    например:.

    telnetd -l /bin/login
    
  • Включить опцию huponexit.

    например:.

    shopt -s huponexit
    

    Введите это в сеансе bash каждый раз или добавьте его в .bashrc или /etc/profile.


Почему происходит гонка?

bash разблокирует сигналы только тогда, когда они безопасны, и блокирует их, когда некоторая часть кода не может быть безопасно прервана обработчиком сигнала.

Такие критические секции время от времени вызывают точки прерывания, и если сигнал принимается при выполнении критической секции, этот обработчик задерживается до тех пор, пока не произойдет следующая точка прерывания или не будет выведен критический раздел.

Вы можете начать копать из quit.h в исходном коде.

Таким образом, кажется, что в нашем случае bash иногда получает SIGHUP, когда он находится в критическом разделе. SIGHUP выполнение обработчика задерживается, а bash читает EOF и заканчивается перед выходом из критического раздела или вызовом следующей точки прерывания.


Ссылка

  • "Управление заданиями" в официальном руководстве Glibc.
  • Глава 34 "Группы процессов, сеансы и управление заданиями" в книге "Интерфейс программирования Linux".

Ответ 2

AFAIK в обоих случаях процесс должен быть убит. Чтобы этого избежать, вам необходимо выпустить nohup следующим образом:

> nohup ./my_app &

Таким образом ваш процесс продолжит выполнение. Вероятно, часть telnet связана с BUG, ​​подобным этому:

https://bugzilla.redhat.com/show_bug.cgi?id=89653

Ответ 3

Когда вы закрываете терминал, оболочка отправляет SIGHUP во все фоновые процессы - и это убивает их. Это может быть подавлено несколькими способами, в первую очередь:

поЬир

Когда вы запускаете программу с помощью nohup, она ловит SIGHUP и перенаправляет вывод программы.

$ nohup app &

открестился

disown сообщает shell не отправлять SIGHUP

$ app &
$ disown

Зависит ли он от версии Linux?

Это зависит от вашей оболочки. Выше применяется, по крайней мере, для bash.

Ответ 4

Чтобы полностью понять, что происходит, вам нужно немного поработать внутри unix.

Когда вы запускаете команду, подобную этой

./app_name &

app_name отправляется в фоновый процесс. Вы можете проверить unix группы процессов здесь

Когда вы закрываете bash с нормальным выходом, он вызывает SIGHUP сигнал зависания на все свои задания. Некоторая информация о контроле работы unix здесь.

Чтобы ваше приложение работало, когда вы выходите из bash, вам нужно сделать приложение неприкосновенным к сигналу зависания с помощью утилиты nohup.

nohup - запустить команду, не зависящую от зависаний, с выходом на не-tty

И, наконец, так вам нужно это сделать.

nohup app_name & 2> /dev/null;