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

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

./app_name &

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

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

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

Я запустил эту программу на локальном компьютере с Linux в фоновом режиме и закрыл терминал.

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

Мой вопрос:

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

5 ответов

Решение

Кто должен убивать рабочие места?

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


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

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

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

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

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

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

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

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

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


Когда делает bash Отправить SIGHUP?

Баш отправляет SIGHUP на все рабочие места (передний план и фон):

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

Смотрите подробности здесь.

Заметки:

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

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


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

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


Telnet или SSH

Под telnet или SSH, следующее должно происходить, когда соединение закрыто (например, когда вы закрываете telnet окно на ПК):

  1. клиент убит;
  2. сервер обнаруживает, что клиентское соединение закрыто;
  3. сервер закрывает главную сторону pty;
  4. ядро обнаруживает, что мастер pty закрыт и отправляет SIGHUP в bash;
  5. bash получает SIGHUP, отправляет SIGHUP на все рабочие места и прекращается;
  6. каждая работа получает SIGHUP и заканчивается.

проблема

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

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

  1. обычно, bash получает SIGHUP и немедленно убивает фоновые задания (как и ожидалось) и прекращает работу;
  2. но иногда, 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 вариант.

    Например:

    шоп-хупонексит
    

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


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

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

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

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

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


Ссылка

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

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

поЬир

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

$ nohup app &

отрекаться

disown говорит оболочке не отправлять SIGHUP

$ app &
$ disown

Это зависит от версии Linux?

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

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

> nohup ./my_app &

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

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

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

Когда вы запускаете такую ​​команду

./app_name &

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

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

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

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

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

nohup app_name & 2> /dev/null;

В современном Linux, то есть в Linux с systemd, есть еще одна причина, по которой это может произойти, о которой вы должны знать: «задержка».

убивает процессы, оставшиеся запущенными из оболочки входа в систему, даже если процесс должным образом демонизирован и защищен от HUP. Это поведение по умолчанию в современных конфигурациях systemd.

Если вы запустите

      loginctl enable-linger $USER

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

остается постоянным до тех пор, пока он не будет снова отключен. Вы можете проверить это с помощью

      ls /var/lib/systemd/linger

Это могут быть файлы, по одному на имя пользователя, для пользователей, у которых есть enable-linger. Любой пользователь, указанный в каталоге, может оставить фоновые процессы запущенными при выходе из системы.

Другие вопросы по тегам