Идея написания данной статьи возникла практически одновременно с моим удивленным взглядом на экран монитора и неуверенным бормотанием: «А где же /etc/inittab?». Не помню уж точно зачем, мне понадобилось выяснить, на каком уровне грузится моя Ubuntu 7.10 — the Gutsy Gibbon. Не найдя традиционного /etc/inittab я некоторое время находился в ступоре. Уже потом, поковыряв google, нашел ответ на мой, как оказалось, простой вопрос.
Традиционный init
Вообще, если попытаться описать процесс загрузки Linux в двух словах, то это будет звучать примерно так. После включения компьютера, загрузки ядра в память, монтирования корневой файловой системы, опроса и инициализации оборудования передача управления по дальнейшей загрузке ОС передается специальному демону, называемому init. Задача init — это запуск всех остальных процессов, нужных для корректного функционирования ОС. В принципе, при помощи параметра ядра init можно указать ядру запускать все, что нам заблагорассудится (что в некоторых специфических дистрибутивах Linux и сделано), однако практически во всех традиционных дистрибутивах запускается именно демон init. Идея на самом деле очень простая и классная. Зачем ядру заморачиваться над запуском хреновой тучи приложений, если достаточно позаботиться лишь о запуске одного процесса, который и будет разруливать дальнейшую загрузку/работу/остановку системы.
Традиционный демон init определяет 7 так называемых «уровней выполнения». Для каждого такого уровня должен быть определен вполне определенный набор системных служб, запускаемых во время загрузки, получающих команды во время работы и останавливаемых во время перезагрузки системы или выключения питания:
- 0 — система полностью прекратила работу
- 1 или S — однопользовательский режим
- 2...5 — многопользовательские режимы
- 6 — перезагрузка системы
Следует отметить, что перечисленный выше список — это всего лишь традиция. То есть, никто не мешает вам для каждого уровня определить свой набор служб и пользоваться в свое удовольствие.
После запуска, демон init читает конфигурацию из файла /etc/inittab и на основании этой конфигурации начинает уже, так сказать, непосредственную деятельность. Грубо говоря, в файле /etc/inittab содержатся указания о том, что должен делать демон init на каждом из уровней.
В какой-то момент формат файла /etc/inittab оказался слишком устаревшим и перестал обеспечивать требуемую разработчиками функциональность, после чего в Linux появился каталог /etc/init.d, содержащий в себе файл /etc/init.d/rc
shell-сценарии запускающие все необходимое, а файл /etc/initttab стал выполнять функцию хранилища номера уровня по-умолчанию и вызова файла /etc/init.d/rc при смене уровней работы системы.
В каталоге /etc/init.d находятся сценарии запуска/перезапуска/останова системных служб. Все эти сценарии могут вызываться с разнообразными параметрами, обязательными из которых являются параметры start и stop. Нужно вам, например, запустить сервер sshd — вы просто даете команду /etc/init.d/sshd start и shell-сценарий делает все необходимо для запуска сервиса. Нужно остановить? Пожалуйста: /etc/init.d/sshd stop. Сам демон init работает не напрямую со сценариями, а следующим образом.
Существует несколько каталогов /etc/rcX.d, где X — это номер уровня работы системы. В каждом из этих каталогов находятся символьные ссылки на сценарии из каталога /etc/init.d. Довольно удобно, поскольку если нужно будет внести изменения в скрипт, то только в одном месте.
Каждая ссылка в каталоге /etc/rcX.d имеет вид YNNname, где Y — определяет параметр, передаваемый скрипту, NN — двузначный номер, name — имя файла сценария работы со службой из каталога /etc/init.d. Y должна быть одной из букв S или K, соответственно определяя, start или stop передавать скрипту. NN — задает очередность выполнения скриптов. Таким образом, при переходе на другой уровень работы системы, init сперва выполняет все K-сценарии в соответствующем текущему уровню каталоге /etc/rcX.d и потом выполняет все S-сценарии в соответствующем уровню, на который переходит система, каталоге /etc/rcX.d.
Для переключения между различными уровнями работы системы существует утилита telinit, которая умеет сообщать демону init, на какой уровень нужно перейти.
Таким вот, в принципе, несложным образом и живет ОС Linux от запуска до остановки. Заранее прошу прощения за мой местами кривой стиль изложения материала. Всех заинтересовавшихся более подробно темой работы демона init отправляю к man-страницам init (8), inittab (5), telinit (8), runlevel (8).
Что такое Upstart и чем он лучше
Начиная с Ubuntu 6.10 старый добрый init был заменен более функционально-продвинутым аналогом, которое авторы назвали Upstart. Основной козырь Upstart в том, что его работа основана на событиях. Это означает, что, в отличие от init, Upstart запускает и останавливает задачи не просто вызывая соответствующие shell-скрипты, а еще и наблюдает за работой запущенных им задач, основываясь на событиях, получаемых им от приложений. Это дает возможность, например, перезапустить в случае чего внезапно упавшую службу самим демоном Upstart, а не возлагать это на какие-то потусторонние программы.
Прежде чем продолжать дальше, хочется отметить, что в терминологии Upstart существует два понятия: служба (service) и задача (task). Главное отлицие службы от задачи состоит в том, что служба перезапускается в случае внезапного ее завершения, а задача — нет. Краткий перечень основных возможностей Upstart, взятый на сайте проекта:
- задачи и службы запускаются и останавливаются при помощи событий
- в момент запуска/остановки службы или задачи генерируется событие
- события могут быть получены от любого процесса в системе
- при падениях службы могут автоматически перезапускаться
- двунаправленный обмен данными с демоном init с целю опроса состояний запущенных служб, выяснения причин их останова и тому подобное.
Из функционала, который только планируется в будущих версиях Upstart на сайте отметили следующие моменты:
- генерация событий через определенный интервал времени или с использованием планировщика
- генерация событий в ответ на изменение содержимого файлов/каталогов
- наблюдение и перезапуск демонов процессы которых отделены от родительских
- возможность непривилегированным пользователям создавать свои собственные службы и управлять ими
- связь с демоном init средствами DBUS
Установка UpStart
Первым делом, проверяем, соответствует ли наша система требованиям:
- Linux >= 2.6.17
- GCC >= 4.1
- glibc >= 2.4
Если все хорошо, посещаем http://upstart.ubuntu.com/download.html и выкачиваем последнюю версию Upstart. Распаковываем архив, куда удобно, переходим в образовавшееся дерево каталогов и файлов исходного кода и начинаем колдовать.
Конфигурируем исходный код для компиляции:
./configure --prefix=/usr --exec-prefix=/ --sysconfdir=/etc --enable-compat=sysv
О дополнительных возможных параметрах конфигурирования можно узнать из файла INSTALL, находящегося непосредственно в корне дерева исходных кодов.
Собираем:
make
Устанавливаем:
make install
Описание заданий
После успешной установки Upstart, необходимо создать определения заданий для того, чтобы система смогла загрузиться. Другими словами, задания как раз есть, что чем оперирует Upstart в своей работе. Чтобы быстрей понять, как это все делается и, так сказать, увидеть своими глазами, можно скачать архив примеров заданий. Возможно, вам придется их немного видоизменить для корректной работы с вашей системой, однако все необходимое для начальной конфигурации системы и успешного первого запуска в архиве примеров есть. Все примеры необходимо распаковать в каталог /etc/event.d. Именно из него Upstart берет все необходимое для работы (про /etc/inittab все дружно забыли). За исключением моментов описанных в разделе «Upstart on Other Distributions» все скрипты в каталогах /etc/rcX.d можно оставить без изменений. все должно заработать.
Последний штрих и перезагрузка
Когда все готово, можно попробовать перегрузить Linux и посмотреть, что у вас получилось. Перед перезагрузкой не забудьте проверить значение параметра init, передаваемого ядру вашим загрузчиком, в случае, если Upstart установил исполняемый файл init в отличный от /sbin каталог. Вообще, в принципе, настоятельно рекомендуется для начала не заменять стандартный sysvinit на init Upstart, а установить его в какой-то другой каталог и при помощи параметра ядра init сперва все тщательно протестировать.
Пишем задания
Тем, кому необходимо расширить стандартный набор заданий или просто интересно знать, как все это сочиняется и работает.
Сразу обращу ваше внимание на то, что на сегодняшний день формат файла задания в Upstart считается еще сырым и может в будущем претерпевать изменения, о чем и сообщается на сайте. Так что, будьте готовы, в случае чего, погрузиться в чтение документации к новым версиям и приведению в соответствие написанных вами ранее заданий.
Все задания помещаются в файлы, расположенные в каталоге /etc/event.d. Имена файлов должны соответствовать именам заданий и сами файлы не должны быть исполняемыми.
Один или более пробелов в тексте файла будут обрабатываться как один пробел, если только эти пробелы не заключены в одинарные или двойные кавычки. Переводы строки разрешены только в пределах кавычек или если перед переводом строки поставить обратный слеш. Также, подобно bash-скриптам, разрешены комментарии, начинающиеся с символа решетки.
exec и script
Каждое задание обязано иметь запись script или exec. Эта запись определяет способ запуска задания. exec используется в случае, когда вы запускаете какую-то программу с вашей файловой системы с возможностью передачи дополнительных параметров вызываемой программе.
exec /bin/foo --opt -xyz foo bar
Запись script используется для вставки bash-кода непосредственно в файл задания. Все, что вы здесь укажете, будет выполнено стандартным интерпретатором /bin/sh с параметром -e, таким образом позволяя прервать скрипт в случае неправильного использования любой команды. Запись script должна заканчиваться строкой «end script».
script # do some stuff if [ ... ]; then ... fi end script
pre-start script и post-stop script
В дополнение к стандартным записям exec и script существует возможность создавать записи pre-start и post-stop. Данную возможность удобно использовать в случае необходимости каких-либо подготовительных действий перед запуком самого скрипта или исполняемого файла, а также при необходимости выполнять какие-то действия по их завершению. Очень важно понимать, что Upstart подразумевает, что выполняемый в этой записи скрипт или бинарный файл обязательно завершат свою работу, а не буду болтаться запущенными. Обязательно учитывайте это при написании заданий!
Примеры:
pre-start script # prepare environment mkdir -p /var/run/foo end script
post-stop script # clean up rm -rf /var/run/foo end script
start on и stop on
Данная запись позволяет вам указать, при возникновении каких именно событий должно выполняться или останавливаться ваше задание.
Самое первое событие, которое генерирует Upstart — это событие startup. Данное событие происходит в тот момент, когда корневая файловая система еще не доступна для записи и отсутствует поддержка сети.
В случае с заданиями из архива с примерами, имеется также событие runlevel X, где X может быть числом от 1 до 6 или символом S. При генерации таких событий будут выполняться init-скрипты, соответствующие уровню запуска X.
И, наконец, вы можете управлять своими заданиями на основе событий, возникающих, в процессе запуска/останова других заданий. Для этого вы можете использовать события stopped и started.
Для определения событий, по которым ваше задание должно запускаться используйте запись start on, останавливаться — stop on. Все просто!
start on startup start on runlevel 2 start on runlevel 3 start on stopped rcS start on started tty1
console
Эта запись используется для указания заданию, откуда оно должно брать ввод и вывод для работы. Значение параметра console может быть: output (в этом случае используется устройство /dev/console), owner (тоже самое, только с возоможностью послать процессу Ctrl+C) и none (это значение является значением по-умолчанию, иползуется устройство /dev/null).
exec echo example console output
Управление заданием
start и stop
Задания можно запускать и останавливать при помощи программ start и stop, размещенных в каталоге /sbin. Каждой из них необходимо передать в качестве параметров список заданий для обработки. Результаты своей работы обе программы предоставляют в поток стандартного вывода.
# start tty1 tty1 (start) running, process 7490 active
# stop tty1 tty1 (stop) running, process 7490 killed
status
Программа status позволяет, как видно из названия, узнать состояние, в котором находится задание.
# status tty1 tty1 (stop) waiting
# start tty1 tty1 (start) running, process 4418
# status tty1 tty1 (start) running, process 4418
Сначала выводится название задания, затем последняя операция, которая выполнялась над заданием (запуск/останов), текущее состояние и идентификатор процесса, если таковой имеется.
initctl list
Список всех заданий и их состояний на текущий момент можно получить при помощи команды initctl list.
# initctl list control-alt-delete (stop)waiting logd (start) running, process 2347 rc-default (stop) waiting rc0 (stop) waiting rc0-halt (stop) waiting rc0-poweroff (stop) waiting rc1 (stop) waiting rc2 (stop) waiting rc3 (stop) waiting rc4 (stop) waiting rc5 (stop) waiting rc6 (stop) waiting rcS (stop) waiting rcS-sulogin (stop) waiting sulogin (stop) waiting tty1 (start) running, process 4418 tty2 (start) running, process 7367 tty3 (start) running, process 7368 tty4 (start) running, process 7369 tty5 (start) running, process 7370 tty6 (start) running, process 7371
initctl emit
При помощи это команды у вас есть возможность генерировать любое событие. Очень удобная штука при написании собственных заданий. Например, имеется задание:
on bounce exec echo --Bounced-- console output
Следующая команда заставит его выполниться
# initctl emit bounce # --Bounced--
Заключение
Вот так, в двух словах, получилась, я надеюсь, статья об Upstart, состоящая процентов на 80 из вольного перевода страницы «Getting Started» с официального сайта Upstart. Have Fun! :-)