Библиотека сайта rus-linux.net
Gentoo: сценарии начальной загрузкиАлексей Федорчук
Содержание
Предисловие Эта заметка - также внеплановая, и возникла, дабы избавиться от угрызений совести. После статьи об установке Gentoo, среди прочей моей корреспонденции было письмо о том, что процесс конфигурирования консольной мыши освещен однобоко (с чем я не могу не согласиться). И, более того, предлагалось содействие в написании материала о сценариях инициализации системы. Попытка ответить на письмо сразу успехом не увенчалась (сообщение об ошибке), а потом, вследствие хронической формы апгрейда (не путать с хроническим склерозом) письмо я просто потерял. Так что (миль пардон перед отправителем - даже ника не запомнил) именно эту заметку прошу считать за ответ. Сначала я собирался (после завершения работы над субциклом о портежах) просто сделать перевод раздела официальной документации Gentoo - об rc-scripts. Однако в процессе этого он разбух моими комментариями и некоторыми примерами, так что весьма далеко отошел от исходного документа (каковой, однако, составляет его основу - думаю, переводные фрагменты от собственных соображений не трудно отличить по стилю). После чего осталось только дать Введение общего характераСистемы инициализации различных дистрибутивов Linux - вопрос, слабо освещенный в русскоязычной литературе. Собственно, мне известна чуть ли не единственная статья, специально ему посвященная - Владимира Попова. Основываясь на которой, я и постараюсь дать некоторое введение в предмет. Инициализация Linux, происходящая после загрузки ядра и монтирования корневой файловой системы (если таковой по каким-то причинам, не происходит, ядро впадает в паническую моду и на этом все кончается), осуществляется посредством запуска процесса init и сводится к исполнению некоторого набора инструкций, описанных в файле /etc/inittab. В системе инициализации SycV, принятой в Linux, существует несколько наборов таких инструкций, сгруппированных в соответствие с понятием т.н. уровней исполнения (runlevels). Понятие уровня исполнения, на мой взгляд, является довольно сложным для, пардон за тавтологию, понимания начинающим пользователем. Интуитивное о нем представление (как и буквальный перевод с английского) способно только затемнить существо дела. В руководстве по Linux Startup (см. библиографию) runlevel определяется как "программная конфигурация системы, которая допускает существование только некоторой выбранной группы процессов". Поэтому примем как данность, что уровень выполнения - просто нумерованный (и - именованный) набор определенных системных сервисов (сетевых, сервиса консольной мыши и т.д.). Наборы эти вызываются на исполнение процессом init в соответствие с их описанием в упомянутом файле /etc/inittab, причем один из них помечен как уровень по умолчанию. Сам по себе процесс init поддерживает шесть уровней исполнения, но привязанные к ним наборы сервисов (и имена уровней) зависят от конкретного дистрибутива. Для примера - таблица уровней исполнения (заимствовано у Владимира Попова) в таких распространенных Linux-дистрибутивах, как Slackware и Red Hat (в клонах последнего - картина схожая):
В общих чертах смысл каждого уровня понятен из названия (последняя строка - уровень, исполняемый по умолчанию), а в деталях я этого касаться не буду - на сей предмет планируется отдельный материал. Достаточно видеть, что для обоих дистрибутивов состав и порядок уровней исполнения задан жестко - это существенно для понимания отличий системы инициализации, принятой в Gentoo. Следует подчеркнуть еще раз, что сам по себе термин runlevel отнюдь не означает, будто бы система при загрузке проходит последовательно сначала однопользовательский (single user) уровень, потом, скажем, - многопользовательский без поддержки сети (вернее, сетевой файловой системы - Multiuser, without NFS), потом - нормальный многопользовательский (Full multiuser mode). Аналогично и с X11 - если этот уровень установлен по умолчанию, это означает (помимо всего прочего) авторизацию в графическом режиме посредством одного из сервисов - KDM/GDM/XDM (что и подчеркнуто в названии этого уровня в Slackware). Gentoo-введениеДистрибутив Gentoo Linux использует систему инициации, в значительной мере основанную на контроле зависимостей. Она является легкой в поддержке, мощной и гибкой в установке. И самое главное, что эти слова из официальной документации - истинная правда. В статье об установке Gentoo было упомянуто, система инициализации в этом дистрибутиве выполнена не в стиле SysV, характерном для большинства Linux-систем (одно из исключений - упомянутый выше Slackware), а в BSD-стиле, в котором, как известно, понятия runlevels отсутствует как класс. Это не значит, конечно, что сами по себе уровни выполнения в Gentoo куда-то делись. Просто в этом дистрибутиве в них вкладывается несколько иной смысл, нежели в большинстве иных Linux-систем. Внешним интерфейсом к системе инициализации Gentoo (как и во FreeBSD, с лову сказать) выступает файл /etc/rc.conf, с которым нам уже приходилось сталкиваться при начальном конфигурировании системы с статье об установке. В нем можно включить или выключить отдельные стартовые сервисы из предопределенного набора, такие, как загрузку клавиатурной раскладки и экранного шрифта, отличных от умолчальных, таблицы перекодировки одного в другое, различные сетевые протоколы. Причем сервисы эти будут загружаться именно в том порядке, в каком они даются в файле /etc/rc.conf (и это - тоже наследие BSD-стиля инициации). При этом сам по себе файл /etc/rc.conf абсолютно ничего не запускает: просто значения его строк присваиваются переменным соответствующих скриптов (каких именно - можно прочитать в комментариях к каждой строке) или будет использоваться как параметр составляющих их команд. Так, включение строки, скажем, KEYMAP="ru4" означает, что в скрипте /etc/init.d/keymaps переменной ${KEYMAP}, употребляемой в качестве параметра команды loadkeys (загружающей клавиатурные раскладки), будет присвоено значение ru4. Так что собственно управление процессом загрузки осуществляется некоторыми сценариями. И тут придется вернуться к вопросу об уровнях выполнения. Уровни выполнения в GentooВ отличие от других систем инициализации, в Gentoo уровни выполнения не имеют фиксированных имен или номеров, но скорее являются заказными именами, переопределяющими стандартные уровни выполнения процесса init. По умолчанию имеется три уровня выполнения, именуемые "boot", "default" и "nonetwork". Уровень выполнения "boot" является стандартным для большинства установок, и, как следует из его имени, является первым уровнем, исполняемым во время загрузки. Следующий уровень, "default", как понятно из его имени, представляет собой главный уровень, исполняемый после загрузки. Последний из уровней, "nonetwork" - обеднен сервисами. Уровни исполнения "обитают" в подкаталогах каталога /etc/runlevels, именованных в соответствие с именами runlevel - /etc/runlevels/boot, /etc/runlevels/default, /etc/runlevels/nonetwork. Содержимое этих подкаталогов - символические ссылки на исполняемые файлы сервисов, принадлежащих каждому уровню. Здесь можно увидеть также подкаталог /etc/runlevels/single, соответствующий однопользовательскому режиму. Он пуст, то есть никаких специфических сервисов (за исключением загрузочных, описываемых уровнем boot) не запускает. По умолчанию на boot-уровне запускаются основные сервисы, которые можно посмотреть просто командой: $ ls /etc/runlevels/boot bootmisc@ clock@ keymaps@ net.lo@ urandom@ checkfs@ consolefont@ localmount@ rmnologin@ checkroot@ hostname@ modules@ serial Именно на сервисы boot-уровня воздействуют настройки в /etc/rc.conf - в приведенном списке мы увидим знакомые по строкам этого файла сервисы keymaps, consolefont, clock. Конечно, список стартовых сервисов может варьировать, но очевидно, что большая часть приведенного будет присутствовать в любой системе. На default-уровне к ним добавляются сервисы: $ ls /etc/runlevels/default local@ metalog@ netmount@ Уровень же nonetwork прибавляет к boot-уровню только один сервис: $ ls /etc/runlevels/boot local@ Как сказано ранее, имена уровней выполнения могут быть произвольно переопределены пользователем, с внесением соответствующих изменений в строки файла /etc/inittab, где для переименованных default уровней должны быть прописаны новые имена. Исключение - уровень выполнения boot, который переименованию не подлежит. В документации по rc-скриптам дается предупреждение, что переименование boot-уровня может привести к краху системы. Вся работа по управлению уровнями выполнения обеспечивается скриптом /sbin/rc (обычным сценарием оболочки /bin/bash). Он может также вызывать переключение "на лету" между т.н. виртуальными runlevels. По умолчанию строки файла /etc/inittab, описывающие уровни выполнения, выглядят следующим образом: si:S:sysinit:/sbin/rc boot l0:0:wait:/sbin/rc shutdown l1:1:wait:/sbin/rc single l2:2:wait:/sbin/rc nonetwork l3:3:wait:/sbin/rc default l4:4:wait:/sbin/rc default l5:5:wait:/sbin/rc default l6:6:wait:/sbin/rc reboot z6:6:respawn:/sbin/sulogin Если нам потребуется ввести собственный уровень выполнения, просто создаем соответствующий каталог: $ mkdir /etc/runlevels/mylevel и изменяем одну из строк l3-l5 соответствующим образом: l5:5:wait:/sbin/rc mylevel после чего назначаем новый уровень исполняемым по умолчанию, заменив строку: # Default runlevel. id:3:initdefault: на # Default runlevel. #id:3:initdefault: id:5:initdefault: Ну а как наполнить новый уровень соответствующими сервисами, будет показано в разделе про rc update. Виртуальные runlevelsПоскольку runlevels в Gentoo не являются статическим отражением (mapped) таковых уровней процесса init, их число может быть больше, чем количество номеров уровней, init'ом поддерживаемых. Это дает пользователю возможность создавать профили или виртуальные runlevels в зависимости от потребностей. Например, пользователи лаптопов могут иметь два уровня выполнения, именуемые, скажем, "online" и "offline". Это - полностью активизированный runlevel, когда PCMCIA NIC вставлены и частично активизированный runlevel, когда их нет. Скрипты PCMCIA могут быть сконфигурированы для вызова как "/sbin/rc online" или "/sbin/rc offline", в порядке старта и останова правильных сервисов, в зависимости от статуса PCMCIA NIC. Уровни выполнения и XFree86В Gentoo мы не имеем runlevel'а, специально посвященного Иксам, но скорее стартовый сценарий. Он зовется xdm и может быть добавлен к любому уровню выполнения по желанию пользователя. Если тому угодно, это может быть главный его уровень. Однако добавление этого скрипта к boot-уровню может иметь непредсказуемый эффект. По умолчанию, если xdm, gdm или kdm исполняются раньше старта процесса getty, Иксы запускаются в ближайшей доступной консоли. На слабых машинах не представляет проблемы, что сервис Desktop Manager'а стартует до окончания исполнения всех уровней процесса init. Процессы getty's успевают запуститься прежде Иксов и последние занимают седьмую (если getty, как обычно, запускается на консолях с первой по шестую) виртуальную консоль, как обычно. Однако на быстрых машинах это не так. Иксы успевают стартовать раньше запуска процессов getty, что обычно приводит к тому, что они занимают вторую консоль. Затем здесь же стартует getty, перехватывает управление клавиатурой, а Desktop Manager, напротив, поддержку таковой утрачивает. Эта проблема решается наличием стартового DM-скрипта на одном из "над-init" уровней, именуемого 'a'. Так как это не есть реальный runlevel, назовем наш xdm-скрипт "telinit a". Таким образом все сервисы runlevel 'a' будут исполняться после таковых текущего уровня, в том числе и после процессов getty. RC-скриптыRC-скрипты - сценарии, которые определяют как базовые функции каждого сервиса, так и их положение в ходе загрузки. Место их прописки - каталог /etc/init.d/. Базовая схема rc-скриптаВ схематическом виде rc-script выглядит следующим образом: #!/sbin/runscript depend() { need bar } start() { ebegin "Starting foo" /sbin/foo eend $? "Failed to start foo" } stop() { ebegin "Stopping foo" kill $(cat /var/run/foo.pid) eend $? "Failed to stop foo" } Как видно из первой строки, rc-script запускается интерпретатором /sbin/runscript. Функция depend является опциональной. Большинству скриптов необходима по меньшей мере функция start. Управление запускомОбщая последовательность запуска сервисов в каждом runlevel'е - алфавитная (вернее, скорее, в порядке ASCII-кодов). Она генерируется последовательностью вывода команды /bin/ls. Основной метод отклонения от умолчальной последовательности запуска сервисов - установка их зависимостей. Альтернативный, если взаимоотношения между сервисами не устанавливаются - задание порядка, в котором они должны использоваться. Типы зависимостейМногие сервисы связаны с другими сервисами или зависят от них. Так, например, сервис Postfix требует установки поддержки сети и системного logger'а. С другой стороны, Samba также требует поддержки сети. Однако если для печати используется CUPS, его демон (cupsd) будет, как правило, запущен раньше samba. Что для запуска последней не критично, но samba для печати будет использовать CUPS. Таким образом мы имеем два вида зависимостей между сервисами - зависимость необходимости (NEED dependency) и зависимость использования (USE dependency). Оба типа действуют всегда, выполняется ли runlevel как единое целое, или каждый сервис запускается и останавливается вручную после загрузки. Зависимости необходимости (NEED-зависимости)Зависимость необходимости устанавливается, если некий сервис критически необходим для старта того сервиса, о котором в данный момент идет речь (current service). Примером является добавление сервисов logger и net для работы Postfix как NEED-зависимости: depend() { need net logger } Порядок перечисления сервисов после NEED критично для запуска текущего сервиса. Последний даст при старте ошибку в случае, если какая-либо из NEED-зависимостей будет нарушена. Некоторые сервисы обязаны стартовать даже в том случае, если они исключены из текущего уровня или boot-уровня. NEED-зависимость рассматривается как более сильная. Зависимости использования (USE-зависимости)USE-зависимость устанавливается, если некий сервис не критичен для запуска текущего, однако им используется (или может использоваться). В этом случае первый должен быть запущен раньше второго. Ниже в примере для сервиса portmap устанавливается USE-зависимость, дабы он мог быть использован сервисом netmount. depend() { use portmap } Netmount по умолчанию предназначен для монтирования NFS, но использует для этого portmap, если тот добавлен к текущему или boot-уровню. В некоторых случаях монтирование NFS устанавливается по умолчанию, в этом случае netmount рассматривает portmap как USE-зависимость, стартовавшую прежде него. Некоторые сервисы USE-типа обязаны быть добавлены в текущий или boot-уровень для того, чтобы эта USE-зависимость использовалась. То есть USE-зависимость является слабой. Если некий сервис USE-типа дал ошибку при старте, текущий сервис все равно стартует, так как сервисы USE-типа могут не быть критичными для старта. Управление порядком запуска помимо зависимостейЕсли между двумя сервисами не обнаруживается зависимостей, но необходимо или желательно обеспечить старт одного из них после другого, могут использоваться соотношения AFTER и BEFORE. Однако оба эти типа действуют только во время смены уровня исполнения. В обоих случаях могут использоваться подстановки маски * для включения всех остальных сервисов. Например, форма depend() { after * } устанавливает старт сервиса local после всех прочих сервисов. The BEFORE order typeВ следующем примере текущий сервис должен стартовать раньше сервиса bar. depend() { before bar } The AFTER order typeА здесь, напротив, текущий сервис стартует после условного сервиса foo: depend() { after foo } Виртуальные сервисыПодобно многим другим штуковинам современного Unix-мира, сервисы могут получать всякие особенности и оттенки. Обычно выбор из определяется юзером или администратором. Системные logger'ы - один из примеров тому. Пользователи Gentoo Linux могут выбрать один из четырех различных логгеров. Все сервисы требуют, чтобы некий системный логгер был запущен перед их стартом, но нет необходимости (NEED) в жесткой зависимости со всеми четырьмя. Хотя использование USE-зависимости было бы слишком слабым. Для этого используются виртуальные сервисы, и этот тип зависимости PROVIDE. PROVIDE-типPROVIDE-тип определяет некий виртуальный сервис по отношению к другим сервисам как связанный NEED- или USE-зависимостью. Так предусматривается для сервиса sysklogd: depend() { provide logger } Виртуальный сервис LOGGERLOGGER - предопределенный виртуальный сервис, который предпочтен из всех системных логгеров. Он может быть использован с любым из типов зависимости - NEED или USE. Виртуальный сервис NETВиртуальный сервис NET, в отличие от LOGGER, не является в полном смысле PROVIDE-сервисом. Для определения сервиса NET как виртуального, он должен:
Для любого валидного net.*-сервиса, переменная $IFACE может быть установлена как имя сетевого интерфейса (например, "eth0" для net.eth0). Опции командной строкиРяд сервисов вызывается с некоторыми умолчальными опциями. К ним относятся все ранее упомянутые, за исключением START и STOP, которые могут быть определены юзером как функции в их rc-скриптах. Функция start() должна быть определена обязательно. Функция stop() менее важна, и ее можно опустить. Обычно юзер определяет только функции start(),stop() и restart(). Пример - старт сервиса httpd. # /etc/init.d/httpd start Опции командной строки должны следовать в определенном порядке, например, pause/start net.eth0: # /etc/init.d/net.eth0 pause start Опции START/STOPSTART - запускает сервис, включая некий сервис, от которого он зависит. STOP - останавливает сервис, включая некий сервис, который от зависит него. Опция RESTARTСервис должен быть запущен путем RESTART для возобновления работы. Это вызывает рестарт сервиса, так же как и всех сервисов, которые от него зависят. Если пользовательская функция restart() определена, юзер может использовать "svc_start()" и "svc_stop()" для старта и останова сервисов. Это имеет силу для всех зависимых сервисов. Опция PAUSEОпция PAUSE останавливает сервис, но, в отличие от опции STOP, не вызывает остановки зависимых сервисов. Опция ZAPВосстанавливает состояние сервиса до останова. Опции INEED/NEEDSMEINEED выводит список сервисов, от которых данный сервиса зависит. NEEDSME выводит список сервисов, которые зависят от данного сервиса. Опции IUSE/USESMEIUSE выводит список сервисов, которые данный сервер использует. USESME выводит список сервисов, которые используют данный сервер. Опция BROKENСписок исполняемых сервисов (если таковые имеются), которые зависят от данного сервиса. Добавление заказных опцийЭто относительно легкий способ добавления собственных опций командной строки. Функция с именем опции должна быть определена в rc-скрипте, и добавляется переменная $opts, как показано в примере: opts="${opts} foo" foo() { ............ } КонфигурированиеВ общем конфигурирование выполняется через установку переменных окружения. Однако они не могут быть определены в одном rc-скрипте, но в одном из трех конфигурационных файлов. Один из них - специфический (собственный) rc-скрипт, два других - глобальные конфигурационные файлы: /etc/conf.d/<имя_спец_rc-скрипта> /etc/conf.d/basic /etc/rc.conf Эти три конфигурационных файла являются источником автоматически, в порядке перечисления. Все NET-сервисы своим источником имеют также файл /etc/conf.d/net. Скрипты-утилитыУтилита rc-updateУтилита rc-update - первейший инструмент для добавления или удаления сервисов в уровне исполнения. Здесь может быть вызван также "depscan.sh" для апдейта зависимостей в кэше. Для примера, добавление сервиса metalog к default-уровню: # rc-update add metalog default Или - напротив, удаление того же metalog default-уровня: # rc-update del metalog default Запуск скрипта rc-update без аргументов вызывает справку по его использованию. Именно скрипт rc-update нужно использовать для наполнения заказного уровня исполнения, а как - покажу в заключительном разделе. Скрипт depscan.shСкрипт depscan.sh рассматривается здесь для полноты картины. Он используется для кэширования зависимостей, которые в основном отражают зависимости между сервисами. Он запускается всякий раз, когда новый rc-script добавляется в /etc/init.d/, но так как rc-update автоматически его вызывает, большинство пользователей необходимости в его запуске не ощущают. Практические приложенияНе уверен, что мне удалось достаточно внятно продемонстрировать достоинства системы инициализации Gentoo (чему виной исключительно мои скудные переводческие способности). И потому вместо заключения просто приведу несколько практических упражнений по решению задач, возникающих обычно перед пользователем на этапе настройки системы. Например, нам требуется включить сервис консольной мыши. В статье про установку Gentoo был описан лобовой способ запуска сервиса gpm - просто из командной строки (или из файла /etc/conf.d/local.start). Более грамотно, однако, было бы использовать rc-update. Для этого после установки gpm из системы портежей $ cd /usr/portage emerge sys-libs/gpm просто даем команду $ rc-update add gpm mylevel которая в ответ сообщает нам, что * gpm added to runlevel mylevel... * Caching service dependencies... [ok ] * rc-update complete. Теперь остается открыть в текстовом редакторе файл /etc/conf.d/gpm и привести его строки #MOUSE=ps2 #MOUSE=imps2 #MOUSEDEV=/dev/psaux #MOUSEDEV=/dev/input/mice в соответствие со своими реалиями. Имеющиеся в файле примеры подходят для многих (если не всех) современных моделей. Для мыши с разъемом PS/2 потребуется снять комментарий с третьей строки MOUSEDEV=/dev/psaux для USB-грызуна - с четвертой: MOUSEDEV=/dev/input/mice Для USB-мыши можно использовать также устройство /dev/usbmouse - это символическая ссылка на /dev/input/mice. Протокол выбирается в зависимости от того, имеется на мыши колесико (снять комментарий со второй строки), или нет (раскомментировать первую строку). Обращаю внимание - и USB мышь с точки зрения используемого протокола будет обзываться либо MOUSE=ps2 (без колеса), либо MOUSE=imps2 (с колесом, и не обязательно родной MS IntelliMouse). Случай com- и bus-мышей потребует более радикальных изменений, но за неактуальностью я его не рассматриваю - см. man (8) gpm. Представление о системе инициализации пригодится и при включении поддержки кириллицы. Мы помним, что прямым редактированием файла /etc/rc.conf в boot-уровень добавляются службы загрузки клавиатурной раскладки и экранного шрифта (кириллических, в наших условиях), а также обеспечивается подключение таблицы перекодировки между ними. К слову сказать, строка CONSOLETRANSLATION по умолчанию закрыта комментарием, и для включения mapscreen его следует не забыть снять. Таким образом мы кириллизуем первую, или, иначе, системную, консоль. Опять же не могу не вспомнить - именно так осуществляется кириллизация системной консоли во FreeBSD. И там этого достаточно - вследствие особенности ее консольного драйвера syscons настройки раскладок, шрифтов и карт соответствия имеют силу и для всех виртуальных терминалов. В Linux - иначе: нам дополнительно потребуется активизация карты соответствия на всех виртуальных терминалах. Для чего в статье про установку также был предложен прямолинейный подход - добавление соответствующей магической последовательности в файл /etc/init.d/local. Однако и здесь можно предложить иной метод, более соответствующий идеологии Gentoo. Начинаем почти также: создаем коротенький скрипт /etc/init.d/consoletrans, который для случая шести консолей будет иметь вид for i in 1 2 3 4 5 6 do echo -ne '(K' > /dev/vc/$i done Далее командой $ chmod a+x /etc/init.d/consoletrans делаем его исполняемым, после чего остается только добавить его к уровню исполнения по умолчанию: $ rc-update add consoletrans mylevel Соответствующая символическая ссылка будет добавлена в каталог /etc/runlevels/mylevel (или, по желанию, default). И теперь после реинициализации системы кириллица будет благополучно выводиться на всех виртуальных консолях. Библиография вопросаКак я уже говорил, в основу этой статьи лег rc-scripts из документации Gentoo. А вообще о системе инициализации можно почитать в весьма подробном Linux Startup Manual. Правда, этот документ посвящен практически исключительно Red Hat. И потому очень полезным дополнением к нему будет статья Владимира Попова Init...etc., которую придется поискать среди старых материалов Софтерры. А о том, когда и почему бывает важен порядок загрузки сервисов (особенно для ноутбуков), можно прочитать в другой статье Владимира. |