This is Gentoo's testing wiki. It is a non-operational environment and its textual content is outdated.

Please visit our production wiki at https://wiki.gentoo.org

Handbook:MIPS/Working/Initscripts

From Gentoo Wiki (test)
Jump to:navigation Jump to:search
This page is a translated version of the page Handbook:MIPS/Working/Initscripts and the translation is 100% complete.
MIPS Handbook
Установка
Об установке
Выбор подходящего источника для установки
Настройка сети
Подготовка дисков
Установка stage3
Установка базовой системы
Настройка ядра
Настройка системы
Установка системных утилит
Настройка загрузчика
Завершение
Работа с Gentoo
Введение в Portage
USE-флаги
Возможности Portage
Система init-скриптов
Переменные окружения
Работа с Portage
Файлы и каталоги
Переменные
Смешение ветвей программного обеспечения
Дополнительные утилиты
Дополнительные репозитории пакетов
Расширенные возможности
Настройка сети
Начальная настройка
Расширенная настройка
Модульное построение сети
Беспроводная сеть
Добавляем функциональность
Динамическое управление


Уровни запуска

Процесс загрузки системы

Когда загружается система по экрану пробегает много текста. Если присмотреться, заметно, что этот текст (обычно) не меняется от загрузки к загрузке. Последовательность всех этих действий называется последовательностью загрузки и в той или иной степени постоянна.

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

Этот процесс удостоверяется, что все файловые системы (определенные в /etc/fstab) смонтированы и готовы к использованию. Затем он выполняет несколько скриптов, находящихся в каталоге /etc/init.d/, которые запускают сервисы, необходимые для нормального запуска системы.

И, наконец, когда все скрипты выполнены, init подключает терминалы (чаще всего просто виртуальные консоли, которые видны при нажатии Alt+F1, Alt+F2 и так далее), прикрепляя к каждой консоли специальный процесс под названием agetty. Этот процесс впоследствии обеспечивает возможность входа в систему через эти терминалы с помощью login.

Init-скрипты

Сейчас процесс init запускает скрипты из каталога /etc/init.d/ не просто в случайном порядке. Более того, запускаются не все скрипты из /etc/init.d/, а только те, которые предписано исполнять. Решение о запуске скрипта принимается в результате просмотра каталога /etc/runlevels/.

Во-первых, init запускает все скрипты из /etc/init.d/, на которые есть символьные ссылки из /etc/runlevels/boot/. Обычно скрипты запускаются в алфавитном порядке, но в некоторых скриптах имеется информация о зависимостях от других скриптов, указывающая системе на необходимость их предварительного запуска.

Когда все скрипты, указанные в /etc/runlevels/boot/, будут выполнены, init переходит к запуску скриптов, на которые есть символьные ссылки из /etc/runlevels/default/. И снова запуск происходит в алфавитном порядке, пока в скрипте не встретится информация о зависимостях; тогда порядок изменяется для обеспечения правильного порядка запуска. Именно по данной причине команды, используемые во время установки Gentoo Linux, используют default, как в команде rc-update add sshd default.

Как работает init

Конечно, init не принимает решений сам по себе. Ему необходим конфигурационный файл, где описаны необходимые действия. Этот файл - /etc/inittab.

Если запомнили последовательность загрузки, описанную чуть ранее, вы вспомните, что первое действие init - это монтирование всех файловых систем. Это определяется в следующей строке файла /etc/inittab:

Файл /etc/inittabСтрока инициализации
si::sysinit:/sbin/openrc sysinit

Этой строкой процессу init предписывается выполнить /sbin/openrc sysinit для инициализации системы. Самой инициализацией занимается скрипт /sbin/openrc, так что можно сказать, что init делает не слишком много - он просто делегирует задачу по инициализации системы другому процессу.

Во-вторых, init выполняет все скрипты, на которые есть символьные ссылки из /etc/runlevels/boot/. Это определяется следующей строкой:

Файл /etc/inittabВыполнение загрузочных команд
rc::bootwait:/sbin/openrc boot

И снова все необходимые действия выполняются скриптом openrc. Заметьте, что параметр, переданный openrc (boot), совпадает с названием используемого подкаталога в /etc/runlevels/.

Теперь init проверяет свой конфигурационный файл, чтобы определить, какой уровень запуска нужно запустить. Для этого из /etc/inittab считывается строка:

Файл /etc/inittabВыбор уровня запуска по умолчанию
id:3:initdefault:

В приведенном примере (который подходит для подавляющего большинства пользователей Gentoo) номер уровня запуска - 3. Пользуясь этой информацией, init проверяет, что нужно выполнить для запуска уровня запуска 3:

Файл /etc/inittabОпределения уровней запуска
l0:0:wait:/sbin/openrc shutdown
l1:S1:wait:/sbin/openrc single
l2:2:wait:/sbin/openrc nonetwork
l3:3:wait:/sbin/openrc default
l4:4:wait:/sbin/openrc default
l5:5:wait:/sbin/openrc default
l6:6:wait:/sbin/openrc reboot

В строке, определяющей уровень 3, для запуска сервисов снова используется скрипт openrc (на этот раз с аргументом default). Опять-таки, обратите внимание, что аргумент, передаваемый скрипту openrc, совпадает с названием подкаталога из /etc/runlevels/.

По окончании работы openrc, init принимает решение о том, какие виртуальные консоли включить и какие команды выполнить в каждой из них:

Файл /etc/inittabОпределение виртуальных консолей
c1:12345:respawn:/sbin/agetty 38400 tty1 linux
c2:12345:respawn:/sbin/agetty 38400 tty2 linux
c3:12345:respawn:/sbin/agetty 38400 tty3 linux
c4:12345:respawn:/sbin/agetty 38400 tty4 linux
c5:12345:respawn:/sbin/agetty 38400 tty5 linux
c6:12345:respawn:/sbin/agetty 38400 tty6 linux

Существующие уровни запуска

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

В Gentoo определено семь уровней запуска: три служебных и четыре определяемых пользователем. Служебные называются sysinit, shutdown и reboot. Действия, совершаемые ими, в точности соответствуют их названиям: инициализация системы, выключение системы и ее перезагрузка.

Определяемые пользователем уровни - это те, которым соответствуют подкаталоги в /etc/runlevels/: boot, default, nonetwork и single. Уровень boot запускает все службы, необходимые системе и используемые всеми остальными уровнями. Остальные уровни отличаются друг от друга запускаемыми службами: default используется для повседневной работы, nonetwork - для тех случаев, когда не требуется сеть, а single используется при необходимости восстановления системы.

Работа с init-скриптами

Скрипты, запускаемые процессом openrc, называются init-скриптами. Каждый скрипт из /etc/init.d/ может запускаться с аргументами start, stop, restart, zap, status, ineed, iuse, iwant, needsme, usesme или wantsme.

Для запуска, остановки или перезапуска службы (и всех, зависящих от нее) следует использовать start, stop и restart:

root #/etc/init.d/postfix start
Заметка
Останавливаются или перезапускаются только те службы, которым необходима данная служба. Остальные зависимые службы (те, которые используют службу, но не нуждаются в ней) эта операция не затрагивает.

Если вы хотите остановить службу, но оставить зависимые от нее работающими, можно использовать опцию --nodeps вместе с аргументом stop:

root #/etc/init.d/postfix --nodeps stop

Чтобы узнать текущее состояние службы (запущена, остановлена, и т.д.), можно использовать аргумент status:

root #/etc/init.d/postfix status

Если указано, что служба работает, но вы знаете, что это не так, можно сбросить состояние на stopped , используя аргумент zap:

root #/etc/init.d/postfix zap

Для того, чтобы выяснить зависимости сервиса, можно использовать аргументы iwant, iuse или ineed. С помощью ineed можно увидеть те сервисы, которые действительно необходимы для правильного функционирования интересующего вас сервиса. С другой стороны, iwant или iuse покажет те сервисы, которые могут использоваться нашей сервисом, но не обязательны для его корректной работы.

root #/etc/init.d/postfix ineed

Аналогично можно узнать, какие сервисы нуждаются в данном сервисе (needsme) или могут ее использовать (usesme или wantsme):

root #/etc/init.d/postfix needsme

Обновление уровней запуска

rc-update

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

Используя rc-update, можно включать и исключать init-скрипты из уровней запуска. Из rc-update автоматически запускается скрипт depscan.sh для перестроения дерева зависимостей.

Добавление и удаление служб

В процессе установки Gentoo вы уже добавляли init-скрипты в уровень запуска default. Ранее в данном документе было объяснено, что означает default. Кроме уровня запуска, скрипту rc-update требуется второй аргумент, определяющий действие: add (добавить), del (удалить) или show (показать).

Для того, чтобы добавить или удалить init-скрипт, просто введите rc-update с аргументом add или del, затем название init-скрипта и уровня запуска. Например:

root #rc-update del postfix default

По команде rc-update -v show выводится список всех доступных init-скриптов с указанием списка уровней запуска, на которых они будут выполняться:

root #rc-update -v show

Вы также можете запустить rc-update show (без -v) чтобы просто просмотреть включенные инициализационные скрипты и их уровни запуска.

Настройка служб

Почему необходима дополнительная настройка

Init-скрипты могут быть весьма сложны. Поэтому нежелательно допускать непосредственное редактирование скриптов пользователями, так как это может привнести в систему множество ошибок. Но, с другой стороны, необходимо правильно настроить сервис. Например, может понадобиться передать самому сервису дополнительные параметры.

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

Каталог conf.d

В Gentoo предусмотрен очень простой способ настройки сервисов: для каждого init-скрипта, предполагающего настройку, в каталоге /etc/conf.d/ есть конфигурационный файл. Например, у скрипта, запускающего apache2 (под названием /etc/init.d/apache2) есть настроечный файл /etc/conf.d/apache2, где могут храниться нужные параметры, передаваемые серверу Apache 2 при запуске:

Файл /etc/conf.d/apache2Пример настроек для скрипта инициализации apache2
APACHE2_OPTS="-D PHP5"

Такие файлы настроек содержат только переменные (наподобие /etc/portage/make.conf), облегчая настройку служб. Это также позволяет нам давать больше информации о переменных (в комментариях).

Написание init-скриптов

Это необходимо?

Нет, написание init-скриптов обычно не требуется, так как Gentoo содержит готовые скрипты для всех предоставляемых сервисов. Однако, некоторые пользователи могут установить какой-либо сервис, не используя систему Portage; в таком случае, вероятно, придется создавать init-скрипт самостоятельно.

Не используйте init-скрипт, идущий с сервисом, если он не написан специально для Gentoo: init-скрипты Gentoo не совместимы со скриптами, используемыми в других дистрибутивах! То есть, если другой дистрибутив не использует OpenRC!

Структура

Основная структура init-скрипта показана ниже.

Код Пример структуры init-скрипта (традиционный)
#!/sbin/openrc-run
  
depend() {
# (Информация о зависимостях)
}
  
start() {
# (Команды, необходимые для запуска сервиса)
}
  
stop() {
# (Команды, необходимые для остановки сервиса)
}
Код Example initscript layout (updated)
#!/sbin/openrc-run
command=/usr/bin/foo
command_args="${foo_args} --bar"
pidfile=/var/run/foo.pid
name="FooBar Daemon"

description="FooBar is a daemon that drinks"
extra_started_commands="drink"
description_drink="Opens mouth and reflexively swallows"

depend() {
#  (Dependency information)
}

start_pre() {
#  (Commands necessary to prepare to start the service)
    # Ensure that our dirs are correct
    checkpath --directory --owner foo:foo --mode 0775 \
        /var/run/foo /var/cache/foo
}
  
stop_post() {
#  (Commands necessary to clean up after the service)
    # Clean any spills
    rm -rf /var/cache/foo/*
}

drink() {
    ebegin "Starting to drink"
    ${command} --drink beer
    eend $? "Failed to drink any beer :("
}

В любом init-скрипте должна быть определена функция start() или переменная command. Все остальные разделы необязательны.

Зависимости

Существуют три настройки, работающие с зависимостями, которые можно определить, и они будут влиять на порядок запуска init-скриптов: want (хочу), use (использую) и need (нуждаюсь). Кроме этих, существуют еще два влияющих на порядок загрузки метода, называющихся before (перед) и after (после). Последние два определяют не зависимости, они не остановят с ошибкой основной скрипт, если тот скрипт, что в них описан, вообще не должен запукаться (или не смог запуститься).

  • Настройка use информирует систему init, что данный скрипт использует функциональность некоторого скрипта, но строго от него не зависит. Хорошим примером будет use logger или use dns. Если эти сервисы есть, они будут хорошо использоваться, но если у вас нет логгера, или DNS-сервера, сервисы все равно будут работать. Если сервисы существуют, они будут запущены до того, как запустится скрипт, использующий их.
  • Настройка want похожа на use с одним исключением. use учитывает только те сервисы, которые были добавлены в какой-либо уровень запуска. want попытается запустить любой доступный сервис, даже если он не был добавлен в один из уровней запуска.
  • Настройка need это жесткая зависимость. Она означает, что скрипт, которому нужен другой скрипт, не запустится, пока другой скрипт не запустится успешно. Также, если другой скрипт будет перезапущен, то этот тоже будет перезапущен.
  • При использовании before, данный скрипт запускается до некоторого скрипта, если выбранный скрипт это часть того же уровня инициализации. Так, инициализационный скрипт xdm, который определен before alsasound будет запущен до скрипта alsasound, но только если alsasound запланирован запуститься на том же уровне инициализации. Если alsasound не запланирован запуститься, то эта конкретная настройка не будет иметь эффекта, и xdm запустится в тот момент времени, который система init посчитает лучшим вариантом.
  • Похожим образом, after информирует систему init, что данный скрипт нужно запустить после некоторого скрипта, если выбранный скрипт является частью того же уровня инициализации. Если нет, то настройка не имеет эффекта, и скрипт будет запущен системой init в момент времени, который, как она посчитает, будет наилучшим.

Из вышенаписанного должно быть ясно, что need это единственная действительная настройка зависимостей, так как она влияет на то, будет ли запущен скрипт или нет. Все остальные являются больше указателями системе init, говорящими в каком порядке скрипты могут (или должны) запускаться.

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

Виртуальная зависимость - это зависимость от функций, предоставляемых сервисом, но не каким-то единственным сервисом. Init-скрипт может зависеть от сервиса системного журнала, но таких достаточно много (metalogd, syslog-ng, sysklogd и тому подобное). Поскольку нельзя нуждаться в каждой из них (ни в одной вразумительной системе они не запущены все сразу), мы обеспечили предоставление виртуальной зависимости всеми этими сервисам.

Например, давайте взглянем на информацию о зависимостях postfix.

Файл /etc/init.d/postfixИнформация о зависимостях сервиса Postfix
depend() {
  need net
  use logger dns
  provide mta
}

Как можно увидеть, сервис postfix:

  • требует сеть (net): виртуальная зависимость, удовлетворяемая, например, /etc/init.d/net.eth0
  • использует журнал (logger): виртуальная зависимость, удовлетворяемая, например, /etc/init.d/syslog-ng
  • использует (dns): виртуальная зависимость, удовлетворяемая, например, /etc/init.d/named
  • предоставляет почтовый агент (mta): виртуальная зависимость, общая для всех программ - почтовых серверов

Порядок запуска

Как мы описали в предыдущем разделе, можно сказать системе init, в каком порядке она должна запускать (или останавливать) скрипты. Этот порядок поддерживается как через настройки зависимостей use и need, так и через настройки порядка before и after. Так как мы описали их ранее, давайте посмотрим на сервис Portmap, как на пример такого init-скрипта.

Файл /etc/init.d/portmapИнформация о зависимостях сервиса portmap
depend() {
  need net
  before inetd
  before xinetd
}

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

Код Использование символа *
depend() {
  before *
}

Если сервис должен писать на локальные диски, он должен потребовать localmount. Если он что-либо поместит в /var/run/, например, pid-файл, он должен запускаться после bootmisc:

Код Настройка зависимостей - нам нужен localmount и мы стартуем после bootmisc
depend() {
  need localmount
  after bootmisc
}

Стандартные функции

Следом за разделом depend() потребуется определить функцию start(). В ней содержатся все команды, необходимые для запуска сервиса. Рекомендуется применять функции ebegin и eend для сообщений пользователю о том, что происходит:

Код Пример функции start()
start() {
  if [ "${RC_CMD}" = "restart" ];
  then
    # Что-нибудь сделать, если рестарт требует больше, чем просто последовательный запуск stop и start
  fi
  
  ebegin "Starting my_service"
  start-stop-daemon --start --exec /path/to/my_service \
    --pidfile /path/to/my_pidfile
  eend $?
}

Как --exec, так и --pidfile должны использоваться в функциях start и stop. Если сервис не создает pid-файл, тогда используйте --make-pidfile, если возможно, хотя лучше протестировать это, чтобы быть уверенным. Иначе, не используйте pid-файлы. Можно добавить --quiet к опциям start-stop-daemon, но это не рекомендуется, если только сервис не очень многословный. Использование --quiet может скрыть информацию если сервис не сможет запуститься.

Другой интересной настройкой, используемой в вышеприведенном примере является проверка содержимого переменной RC_CMD. В отличие от предыдущей инициализационной системы, новая система OpenRC не поддерживает отдельную функциональность restart для каждого скрипта. Вместо этого, скрипт должен проверить содержимое переменной RC_CMD, чтобы проверить, вызывается ли функция (как start(), так и stop()) как часть restart, или нет.

Заметка
Удостоверьтесь, что --exec действительно вызывает сервис, а не shell-скрипт, который запускает сервисы и выходит — это должен делать сам init-скрипт.

Если нужны дополнительные примеры функции start(), пожалуйста, прочитайте исходный код init-скриптов, находящихся в каталоге /etc/init.d/.

Еще одной функцией, которую можно определить (но не обязательно это делать), является stop(). Система init, применяемая нами, достаточно развита и в состоянии самостоятельно заполнить эту функцию, если используется start-stop-daemon.

Код Пример функции stop()
stop() {
  ebegin "Stopping my_service"
  start-stop-daemon --stop --exec /path/to/my_service \
    --pidfile /path/to/my_pidfile
  eend $?
}

Если сервис запускает некоторый другой скрипт (например, на Bash, Python или Perl), и этот скрипт позднее изменяет имя (например, с foo.py на foo), тогда нужно добавить --name к start-stop-daemon. Нужно определить имя, на которое скрипт изменит свое имя. В приведенном примере, сервис запускает foo.py, а потом это имя меняется на foo:

Код Пример определения сервиса, который запускает скрипт foo
start() {
  ebegin "Starting my_script"
  start-stop-daemon --start --exec /path/to/my_script \
    --pidfile /path/to/my_pidfile --name foo
  eend $?
}

У start-stop-daemon есть отличная man-страница, которую можно посмотреть, если нужна дополнительная информация.

user $man start-stop-daemon

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

Добавление дополнительных параметров

Если нужно ввести в init-скрипты дополнительные параметры, кроме упоминавшихся, нужно добавить параметр в одну из следующих переменных и создайте функцию с названием, соответствующим параметру. Например, для поддержки параметра restartdelay:

  • extra_commands - команду можно запустить при любом состоянии сервиса
  • extra_started_commands - команду можно запустить когда сервис запущен
  • extra_stopped_commands - команду можно запустить когда сервис остановлен


Код Пример определения метода restartdelay
extra_started_commands="restartdelay"
  
restartdelay() {
  stop
  sleep 3    # пауза в 3 секунды перед повторным запуском
  start
}
Важно
Функция restart() не может быть переназначена в OpenRC!

Переменные для настройки сервисов

Для поддержки настроечных файлов из каталога /etc/conf.d/ ничего дополнительно делать не нужно: при запуске init-скрипта автоматически подключаются следующие файлы (т.е., переменные из них становятся доступны):

  • /etc/conf.d/ВАШ_INIT_СКРИПТ
  • /etc/conf.d/basic
  • /etc/rc.conf

Кроме того, если init-скрипт предоставляет виртуальную зависимость (например, net), то также подключается файл, соответствующий этой зависимости (например, /etc/conf.d/net).

Изменение поведения уровней запуска

Кто может выиграть?

Большинству пользователей ноутбуков знакома ситуация: дома нужен запуск net.eth0, и наоборот, в дороге запуск net.eth0 не нужен (так как сеть недоступна). В Gentoo можно изменять поведение уровней запуска по своему усмотрению.

Например можно создать второй загружаемый уровень запуска default, в котором будут другие init-скрипты. Затем, при загрузке, можно выбрать, какой из уровней default следует использовать.

Использование программного уровня (softlevel)

Прежде всего, создайте каталог для второго уровня запуска default. Например, создадим уровень запуска offline:

root #mkdir /etc/runlevels/offline

Добавьте необходимые init-скрипты в только что созданный уровень запуска. Например, чтобы получить точную копию уровня default, за исключением net.eth0:

root #cd /etc/runlevels/default
root #for service in *; do rc-update add $service offline; done
root #rc-update del net.eth0 offline
root #rc-update show offline
(Partial sample Output)
               acpid | offline
          domainname | offline
               local | offline
            net.eth0 |

Даже несмотря на то, что net.eth0 был удален с уровня запуска offline, udev может попытаться запустить любые устройства, которые найдет, и запустить соответствующие сервисы. Данная функциональность называется hotplugging. По умолчанию, Gentoo отключает hotplugging.

Чтобы включить hotplugging, но только для конкретного набора скриптов, используйте переменную rc_hotplug в /etc/rc.conf:

Файл /etc/rc.confВключение hotplugging на интерфейсе WLAN
rc_hotplug="net.wlan !net.*"
Заметка
Для более детальной информации о сервисах, инициируемых устройствами, просмотрите комментарии в /etc/rc.conf.

Теперь необходимо отредактировать конфигурацию загрузчика, добавив новую запись для уровня запуска offline. В данной записи добавьте softlevel=offline в качестве параметра загрузки.

Использование загрузочного уровня (bootlevel)

Использование загрузочного уровня полностью аналогично использованию программного уровня. Единственная разница состоит в том, что определяется второй уровень boot вместо второго уровня default.