[Перевод] Кунг-фу стиля Linux: организация работы программ после выхода из системы
Если вы пользуетесь Linux с ранних дней появления этой ОС (или если, вроде меня, начинали с Unix), то вам не надо очень быстро и в больших количествах изучать то новое, что появляется в системе по мере её развития и усложнения. Вы можете разбираться с новым постепенно, в режиме обычной работы. Но если вы только начинаете знакомство с Linux, то вам будет непросто сразу в ней разобраться, сразу понять её особенности. Среди тех, кому приходится изучать Linux с нуля, те, кто пользуется Raspberry Pi, те, кого расстроило то, что Microsoft забросила Windows XP, те, кто развернул облачную среду для своего IoT-проекта, похожего на Skynet.
Недавно сын спросил меня о том, как сделать так, чтобы что-то работало бы на Linux-компьютере даже тогда, когда осуществлён выход из системы. Я подумал, что это — хороший вопрос, и что на него, в зависимости от того, о чём именно идёт речь, может и не быть простого ответа.
Есть четыре ситуации, которые можно рассмотреть, отвечая на этот вопрос:
- Нужно запустить некую программу, выполнение которой, как заранее известно, займёт много времени.
- Была запущена некая программа, а потом стало понятно, что её выполнение займёт много времени. После этого решено было выйти из системы, не прерывая работу этой программы.
- Нужно написать скрипт или другую программу, которая может отключиться от терминала и работать сама по себе (такие программы называют демонами).
- Нужно, чтобы некая программа работала бы всё время, даже сразу после перезагрузки системы, когда в неё ещё не входили.
Одна из особенностей Linux-подобных систем, усложняющих работу с ними, заключается в том, что они дают пользователю множество возможностей. В результате то, что работает в одной системе, в другой может и не работать. Если же говорить об одном и том же дистрибутиве, то тут шанс нахождения более или менее универсальных методик работы повышается. Я, чтобы не отвлекаться, собираюсь тут рассказать, в основном, о первых двух пунктах вышеприведённого списка. Я, кроме того, исхожу из предположения о том, что мы говорим о программах, работающих в режиме командной строки. Если вам нужно, чтобы после выхода из системы работали бы программы с графическим интерфейсом, то по этому поводу у меня возникает много вопросов. Это, определённо, возможно, но это странное желание, так как графическое окружение пользователя исчезнет после выхода из системы (правда, для создания постоянных рабочих столов можно воспользоваться VNC или Nx).
Я сосредоточусь на первых двух пунктах списка, но дам некоторые подсказки касательно двух других вариантов. Итак, программа, которая сама отключается от терминала, это — демон. Этот механизм можно создать самостоятельно, а можно и у кого-нибудь позаимствовать. Сделать так, чтобы программа выполнялась бы всё время, может быть сложно, а может быть и не очень сложно. В большинстве Linux-дистрибутивов есть файл /etc/rc.local
, который запускается с root-правами при запуске системы (как минимум — при нормальном запуске). Сюда можно добавлять команды для автозапуска каких-нибудь программ. Если же требуется, например, создавать собственные сервисы, то тут нужно учитывать особенности системы, знать о том, что именно в ней используется — SystemV, Upstart, OpenRC или Systemd. Возможно, придётся столкнуться и с чем-то другим. Но это — отдельная большая тема.
Обеспечение работы программ после выхода из системы
Вернёмся к первым двум пунктам нашего списка. Представьте, что вам надо запустить программу remote_backup
, и вы знаете о том, что вы её запустите (возможно, войдя в систему по ssh), а потом отключитесь, но при этом она должна продолжать работать. Или вы, возможно, хотите обезопасить себя и сделать так, чтобы она продолжала бы работать даже в том случае, если вы случайно выйдете из системы. В любом случае, при обычном запуске программы из командной строки выход из системы означает остановку программы. Или не означает?
Для этого нужно просто запустить программу так, чтобы она не зависела бы от текущей сессии пользователя. Если вы используете bash и всё у вас настроено правильно, то сделать это можно очень просто. Если для запуска программы в фоновом режиме используется &
, то работать она будет и после выхода из системы. То же самое касается и приостановки работающей программы (CTRL + Z
) с последующим переводом её в фоновый режим с помощью команды bg
. Тут мы видим очередной пример гибкости Linux, возможности решить одну и ту же задачу множеством способов.
Если вы работаете с bash, это значит, что вы можете настраивать оболочку, в том числе — параметр huponexit
. Попробуйте выполнить следующую команду:
shopt | grep huponexit
Если окажется, что опция huponexit
выключена, это значит, что обычный перевод программы в фоновый режим позволит ей пережить завершение вашей сессии. Конечно, если то же самое попробовать в какой-нибудь другой системе, это может не сработать и вам придётся выяснять причины такого поведения системы.
Если вам известно о том, что вы пользуетесь bash с выключенной опцией huponexit
, это значит, что запустить программу в фоне вы можете, просто добавив после команды запуска знак &
:
remote_backup &
Но надёжнее будет явным образом выразить намерение, касающееся работы программы после выхода из системы. Сделать так может понадобиться хотя бы из-за того, что при использовании только что описанного метода после выхода из системы нельзя будет видеть никаких выходных данных программы. Если же вы достаточно предусмотрительны, то программу вы можете запустить с использованием nohup
:
nohup remote_backup
Если вы хотите передать nohup
ещё и какие-то аргументы — перечислите их в конце команды, как это обычно делается при работе с другими программами. Nohup
решает следующие задачи:
- Перенаправляет
stderr
вstdout
. - Перенаправляет
stdout
вnohup.out
(местоположение этого файла в разных версияхnohup
может различаться, в частности, он может находиться по адресу~/nohup.out
). - Перенаправляет
stdin
в нечитаемый файл. - Запускает программу и возвращает управление командной оболочке.
В итоге программа будет работать, но не сможет принимать никаких входных данных и при этом то, что она выводит, будет попадать в nohup.out
. Если в nohup.out
уже что-то есть, новые данные будут добавлены в конец файла.
Причина, по которой выполняются все эти перенаправления, заключается в том, что nohup
обнаруживает то, что каждый из потоков подключён к терминалу. Если что-то уже перенаправлено в файл, nohup
не будет это менять. То есть, например, можно поступить так:
nohup remote_backup >/tmp/backupstatus.log &
Или так:
nohup bash -c 'echo y | remote_backup >tmp/backupstatus.log' &
В последнем случае входные данные будут поступать из конвейера, поэтому nohup
это менять не будет. А весь вывод программы (stdout
и stderr
) попадёт в /tmp/backupstatus.log
.
Исследование nohup
Если вы хотите своими глазами увидеть то, как nohup
воздействует на программы, войдите на Linux-сервер по ssh и выполните следующие команды:
echo '#!/bin/bash' >~/huptest
echo sleep 60 >>~/huptest
echo 'date >/tmp/test.txt' >>~/huptest
chmod +x ~/huptest
~/huptest
До тех пор, пока не истекут 60 секунд, нажмите клавишу клавиатуры, вводящую символ «тильда» (~
). Затем нажмите клавишу, вводящую точку. В SSH тильда — это экранирующий символ (если он находится в начале строки). Воспользуйтесь командой ~?
если хотите узнать подробности об этом. Теперь сделайте небольшой перерыв, выпейте чего-нибудь, и вернитесь к компьютеру через несколько минут. Войдите в систему. Файла /tmp/test.txt
вы не найдёте (если только он уже не был создан, но и в таком случае его содержимое позволит сделать правильные выводы). Это говорит нам о том, что завершение сессии остановило программу, ожидающую истечения 60 секунд для продолжения работы.
А теперь попробуйте такую конструкцию:
~/huptest &
Она сообщит оболочке о том, что ей не надо ждать завершения программы. Если вы пользуетесь bash, то это будет работать в том случае, если опция huponexit
выключена. Вы можете поэкспериментировать, включая (shopt -s huponexit
) и выключая (shopt -u huponexit
) эту опцию.
И, наконец, можно выполнить ту же команду с использованием nohup
:
nohup ~/huptest &
Но даже команда nohup
не идеальна. Если программа, которую мы выполняем, сама перехватывает флаг nohup
, то команда nohup
нам не поможет. Правда, большинство программ не перехватывают этот флаг и nohup
будет работать независимо от используемой оболочки и от её настроек.
Есть множество других способов достижения того же результата. Например, можете исследовать возможности команды at
, которая запускает другие программы в заданное время (скажем — через секунду после текущего момента). При запуске программ будет использоваться sh, а не bash (без выполнения некоторых действий), но они будут работать в автономном режиме.
Отсутствие планирования
Правда, существует пара ситуаций, в которых nohup
нам не поможет. Первая — это когда не планировалось запускать программу в фоновом режиме. Как быть, если что-то было запущено, а потом стало ясно, что операция будет выполняться достаточно долго? Что делать, если понадобилось срочно куда-то отойти? Может случиться и так, что нужно запустить программу, вручную ввести в неё какие-то данные, а потом перевести в фоновый режим.
Возможно, вы знаете о том, что работающую программу можно приостановить, воспользовавшись комбинацией клавиш CTRL + Z
. Bash сообщит, что создано задание, и даст его номер. Потом, используя команду bg
, можно перевести это задание в фоновый режим. Если, например, заданию присвоен номер 3
, то соответствующая команда будет выглядеть так:
bg %3
Если опция huponexit
выключена, то больше ничего делать не надо. Но в общем случае нужно сообщить оболочке о том, что надо либо не отправлять заданию команды HUP
, либо убрать его из таблицы заданий. Для решения обеих этих задач можно воспользоваться командой disown
, встроенной в bash.
При использовании disown
без опций будет осуществлено удаление именованного задания из таблицы заданий. Если вы не хотите заходить так далеко, воспользуйтесь опцией -h
для того чтобы оболочка не передавала бы конкретному заданию сигнал HUP
. Можно ещё воспользоваться опцией -a
, позволяющей воздействовать на все задания, или опцией -r
, которая позволяет обращаться ко всем работающим заданиям. Это — стандартная возможность bash (то есть — справку по ней можно найти в справке по bash), поэтому работать она будет не во всех командных оболочках. После того, как вы обработали фоновую задачу с помощью disown
, вы можете спокойно выходить из системы.
Постоянные сессии
Как это обычно бывает в Linux, существует несколько способов решения одной и той же задачи. Для работы с сессиями можно воспользоваться screen или tmux. Работа с этими утилитами похожа на работу с VNC, когда можно выйти из системы, а потом, войдя в неё, обнаружить, что всё, с чем до этого работали, никуда не делось. Если вы решаете множество задач с использованием командной строки, то можете попробовать byobu (это даст вам приятный интерфейс для screen) или tmux.
Использование tmux
Если вы хотите это испытать — войдите в систему как обычно и запустите screen, tmux или byobu. Снова запустите тестовый скрипт (с &
в конце команды или без использования этого символа). Потом воспользуйтесь вышеописанным фокусом ~.
для «убийства» сессии. Далее, снова войдите в систему и перезапустите ту же программу (то есть — screen, tmux или byobu). Перед вами окажется экран, выглядящий таким же, каким он был в тот момент, когда вы вышли из системы. Это, в целом, довольно полезная возможность. Кроме того, она позволяет организовать работу с несколькими окнами, но в нашем случае это значения не имеет.
Проблема богатства возможностей
Мощь Linux отчасти основана на том, что у пользователей этой ОС есть множество способов решения одной и той же задачи. В этом же кроются и проблемы Linux. Причём, эти проблемы возникают не, например, тогда, когда нужно настроить миниатюрный компьютер Raspberry Pi, находящийся под полным контролем одного человека. Настоящие сложности появляются в ситуациях, когда нужно развернуть что-то на множестве компьютеров, за которыми работают разные пользователи, и на которых, возможно, даже установлены разные дистрибутивы.
Конечно, если стремиться к разработке универсальных решений, это — далеко не единственная проблема. Если нужно писать скрипты, которые смогут успешно работать в самых разных Posix-системах, можно почитать документацию GNU по autoconf. Там можно найти много полезного.
Как вы запускаете Linux-программы, которые должны работать после выхода из системы?