Почему хром-браузер убивается, когда я закрываю терминал, несмотря на nohup?

12

Этот вопрос старый, и я до сих пор не понимаю, почему.


Оригинальный вопрос в 2014 году:

На вкладке Терминал Gnome я запустил

$ nohup chromium-browser &

Но когда я закрываю вкладку терминала, chromium-browserтоже выходит. Не nohupдолжно ли это предотвратить? Жиль сказал:

Можно сказать, что nohup и disown подавляют SIGHUP, но по-разному. nohup заставляет программу изначально игнорировать сигнал (программа может изменить это). nohup также пытается устроить так, чтобы программа не имела управляющего терминала, чтобы ядро ​​не отправляло SIGHUP, когда терминал закрыт. отречение является чисто внутренним для оболочки; это заставляет оболочку не отправлять SIGHUP, когда это завершается.

Так что же, nohup заставляет браузер chromium игнорировать SIGHUP?

Я вижу это и на других исполняемых файлах, таких как Emacs (режим GUI). Но не на xeyes.

Это происходит в Ubuntu 12.04, 32-битном, когда вопрос был опубликован.


Обновление в 2015 году,

Сейчас я использую Ubuntu 14.04, google-chromeвместо chromium-browserустановленного. То же самое, что происходило с Chromium-браузером раньше, также происходит и с Google-Chrome. nohup google-chrome 2>/dev/null &не сохраняет его от закрытия, когда вкладка терминала закрыта. /usr/bin/google-chromeэто ссылка на Баш скрипт /opt/google/chrome/google-chrome. Почему nohupприменяется к сценарию bash не работает? Как мы можем заставить его работать на скриптах bash? А как насчет скриптов Python?

Тим
источник
1
Вы должны рассказать больше о своей среде. Я не воспроизвожу твой контрольный пример.
Jlliagre
С какими другими исполняемыми файлами вы видите это? Попробуйте что-нибудь простое, например, как xeyes.
Уоррен Янг
@WarrenYoung: Emacs (GUI). Но xeyes работает.
Тим

Ответы:

12

Когда вы закрываете окно терминала GNOME, в оболочку, в которой он работал, отправляется SIGHUP. Оболочка обычно отправляет SIGHUP каждой группе процессов, которую она знает, даже если она была запущена, nohupи затем завершается. Если оболочка есть bash, она пропустит отправку SIGHUP любой группе процессов, помеченной пользователем disown.

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

Linux предоставляет некоторые инструменты для проверки настроек сигналов запущенного процесса.

Сценарий оболочки chromium-browser выполняет execскомпилированное приложение, поэтому его идентификатор процесса остается прежним. Поэтому, чтобы увидеть настройки сигнала, я побежал, nohup chromium-browser &а затем посмотрел, /proc/$!/statusчтобы увидеть расположение сигнала.

SigBlk: 0000000000000000
SigIgn: 0000000000001000
SigCgt: 0000000180014003

Это шестнадцатеричные числа. Это показывает, что SIGHUP не перехватывается и не игнорируется. Только SIGPIPE (13-й бит в SigIgn) игнорируется. Я проследил это до следующего кода :

// Setup signal-handling state: resanitize most signals, ignore SIGPIPE.
void SetupSignalHandlers() {
  // Sanitise our signal handling state. Signals that were ignored by our
  // parent will also be ignored by us. We also inherit our parent's sigmask.
  sigset_t empty_signal_set;
  CHECK(0 == sigemptyset(&empty_signal_set));
  CHECK(0 == sigprocmask(SIG_SETMASK, &empty_signal_set, NULL));

  struct sigaction sigact;
  memset(&sigact, 0, sizeof(sigact));
  sigact.sa_handler = SIG_DFL;
  static const int signals_to_reset[] =
      {SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV,
       SIGALRM, SIGTERM, SIGCHLD, SIGBUS, SIGTRAP};  // SIGPIPE is set below.
  for (unsigned i = 0; i < arraysize(signals_to_reset); i++) {
    CHECK(0 == sigaction(signals_to_reset[i], &sigact, NULL));
  }

  // Always ignore SIGPIPE.  We check the return value of write().
  CHECK(signal(SIGPIPE, SIG_IGN) != SIG_ERR);
}

Несмотря на комментарий, игнорируемые родителем сигналы не игнорируются. Вздох убьет хром.

Обходной путь должен сделать то, на что указывает @ xx4h: используйте disownкоманду в вашем bash, чтобы, если bash должен выйти, он не отправлял SIGHUP группе chromium-browserпроцессов. Вы можете написать функцию для этого:

mychromium () { /usr/bin/chromium-browser & disown $!; }
Марк Плотник
источник
Благодарю. Теперь я понимаю, в основном, что вы имели в виду. «Поскольку скрипт никогда не сбрасывает эту ловушку, фактический бинарный файл хрома вызывается с настройкой SIGHUP по умолчанию». Под «сбросом этой ловушки» вы подразумеваете замену ловушки для SIGHUP обратно на настройку по умолчанию, которая завершает процесс? Как вы "сбросить эту ловушку"?
Тим
О, под «сбросить эту ловушку» я имел в виду «вернуть расположение этого сигнала обратно так, как оно было до того, как сценарий оболочки установил для него обработчик». Если бы оболочка это сделала trap "" 1, это заставило бы оболочку (и ее дочерние элементы) игнорировать SIGHUP. Я уточню это.
Марк Плотник
Благодарю. Проверю это после вашего уточнения. Сейчас я пытаюсь понять демона, nohup, disown и фон, поэтому я вернулся, чтобы вернуться к своим старым вопросам и ответам, которые я не понимал. Путаница с nohup, disown и background в основном решена, и я застрял с концепцией демона и с тем, как демонизировать процесс (см. Ниже). Я ценю еще раз, если у вас есть время, чтобы помочь снова.
Тим
Я посмотрел на код приложения chromium, и оказалось, что код C ++ в приложении безоговорочно сбрасывает SIGHUP к значению по умолчанию. Эти trapкоманды в сценарии оболочки обертка не препятствует этому, и бегущая nohupне могут предотвратить это. Я пересмотрю свой ответ, чтобы отразить это.
Марк Плотник
3

Если chromium-browserчто-то похожее, google-chromeто я думаю, что наиболее вероятная проблема заключается в том, что chromium-browser это неchromium оболочка оболочки, которая инициализирует состояние, а затем execs chromium.

В моей google-chromeустановке бинарный файл фактически находится, /opt/google/chromeа обертка /usr/bin- это всего лишь сценарий оболочки, который настраивает множество сред, касающихся xdg-*значений по умолчанию и абсолютных путей и аналогичных перед заменой самого бинарного файла.

В этот момент любые сигналы, которые nohupмогли быть изначально проигнорированы от имени скрипта, который он назвал своим дочерним элементом, перестанут иметь значение, и если скрипт-обертка не соблюдает правила, иначе (не так) наследуется ctty.

Попробуйте file /usr/bin/chromium-browserпроверить, является ли это сценарий оболочки, я думаю, что это так. Если это так, подумайте над тем, чтобы переписать его так, чтобы он вам больше подходил.

Я могу сказать, что только выполнение делает google-chrome 2>/dev/null &это открытым для меня, но я не могу вспомнить, является ли это вероятным результатом изменений, которые я сделал в сценарии - это было более года назад.

mikeserv
источник
Благодарю. То же, что произошло chromium-browserраньше, также происходит и google-chromeсейчас. Я не chromium-browserустановил. nohup google-chrome 2>/dev/null &не сохраняет его от закрытия, когда вкладка терминала закрыта. google-chromeэто Баш скрипт /opt/google/chrome/google-chrome. Почему nohup, примененный к bash-скрипту, не работает? Как мы можем заставить его работать на скриптах bash? А как насчет скриптов Python?
Тим
Ну, это делает работу над сценарием, но скрипт работает только в течение нескольких наносекунд , прежде чем заменить фактической chromeдвоичным.
mikeserv
вы говорите в сценарии есть execна самом деле chromeдвоичный файл? Как мне изменить скрипт тогда? Вот мой/opt/google/chrome/google-chrome
Тим
@ Тим - да, видишь дно? exec -a "$0" "$HERE/chrome" ... || exec -a "$0" "$HERE/chrome"... Вверху $HEREустановлено значение readlink $0 (для которого вы устанавливаете путь google-chrome), но /opt/google/chrome/chromeэто двоичный файл, которым скрипт заменяет себя.
mikeserv
@Tim: Что касается того, как идет - вы можете просто отбросить, execвероятно. Вы получите дополнительный pid (больше, чем 1000 других) и ожидающий процесс оболочки, но я думаю, что это предел. Или вы могли бы заменить execж / nohupможет быть. Все зависит главным образом от того, что chromeбудет терпеть - но это должно работать так.
mikeserv