Наши партнеры

UnixForum






Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

На главную -> MyLDP -> Тематический каталог -> Процесс начальной загрузки системы

Init...etc.

Автор: Владимир Попов, popov_inm@yahoo.com
Опубликовано: 27.8.2002


c 2002, Издательский дом <КОМПЬЮТЕРРА> | http://www.computerra.ru/
Журнал <СОФТЕРРА> | http://www.softerra.ru/
Этот материал Вы всегда сможете найти по его постоянному адресу: http://www.softerra.ru/freeos/19771/

Упомянув в статье "Linux: на ноутбуке?" два стиля загрузки (BSD vs SysV), я не предполагал, что эта тема потребует дальнейшего обсуждения. Уже хотя бы потому, что подавляющее большинство популярных Linux-ов используют SysV-стиль. Однако <волна> новых дистрибутивов, появившихся в последнее время (весьма оперативно представленных читателям Softerra Алексеем Федорчуком), и возникающие при их инсталляции вопросы изменили моё отношение к этой теме. Окончательное же решение <взяться за перо> пришло после прочтения статьи уважаемого мной всё того же Алексея Федорчука "Из чего только сделаны Linux-ы". Эта статья, на мой взгляд, - один из немногих примеров материала, способствующего не разрешению конкретных трудностей, таких нередких при общении с Linux, а улучшению понимания работы системы в целом. Дело в том, что материалы такого рода, по моему скромному мнению, полезнее любых конкретных рекомендаций. Жаль, что "неофиты" Linux часто не обращают на них внимания.

Об этом - чуть подробнее. Опять же, по моему скромному мнению, инсталляция, да и эксплуатация Linux принципиально отличаются от таковых для ОС от MicroSoft (по крайней мере, для ОС семейства <9x>). При инсталляции последних подразумевается, что проводящий инсталляцию - пользователь рабочей станции (а то и отдельно стоящего компьютера), абсолютно не обременённый представлениями о локальных сетях, доменах, аппаратных средствах и т.д. и т.п. При инсталляции же Linux сплошь и рядом подразумевается, что её проводит администратор <средней> руки, как минимум. А как иначе, если сами собой подразумеваются установка сетевого программного обеспечения, терминального сервера, серверов http, ftp, SQL, nfs и т.д.? Что за Linux без этого? Можно, конечно, скрыть это всё как <умолчание>, но беда в том, что и дальнейшая эксплуатация системы подразумевает некоторый уровень компетенции, что и не удивительно: администрировать приходится многопользовательский сервер, пусть даже используемый, в данном случае, в качестве рабочей станции.

Складывается ситуация, напоминающая <Уловку-22> Джозефа Хеллера. Напомню: 22-й параграф перечня показаний для демобилизации гласил, что всякий, отказывающийся от военной службы, уже достаточно разумен, чтобы быть признанным годным к ней. Или проще: если ты не хочешь служить, то значит - здоров и служить - должен, а если служить хочешь, то на сомнения в психической нормальности глаза можно закрыть - сам же напрашиваешься. В любом случае служить будешь. Так и с Linux: для того, чтобы узнать, нужно инсталлировать, но для того чтобы инсталлировать, нужно знать. В любом случае знать - нужно. По аналогии я бы назвал это <Уловкой от Linux>.

Системы для конечного пользователя на основе Linux создавать, конечно, можно. Их эксплуатационные характеристики могут быть очень высоки, по крайней мере выше, чем у <свежеинсталлированных> win'9x. Но это отнюдь не означает, что такие системы <автоматически> получаются из дистрибутивов. Быть может, такие дистрибутивы ещё и появятся (необходимость их появления - уже другой вопрос), но в настоящее время большинство <устоявшихся> пользователей Linux, мне кажется, скорее специалисты, нежели <обычные пользователи>. А специалист отличается от пользователя среди прочего ещё и тем, что способен искать решение, не имея конкретных рекомендаций. Их ему заменяют исследовательский азарт, ну, и знания, конечно. Знания, как правило, более общего порядка, нежели опции и местоположение того или иного конфигурационного файла. Администраторам NT-серверов хорошо знакомо раздражение, испытываемое при переходе на новую версию: сервер-то конфигурировать надо, будь он хоть NT, хоть UNIX, а найти необходимые настройки в бесконечно развивающемся <интуитивно понятном> интерфейсе бывает ох как непросто. В Linux ситуация ещё <веселее>: способы конфигурирования разнятся не только для разных дистрибутивов, но и от версии к версии. Особые решения могут потребоваться, если вы захотите использовать внедистрибутивное ПО. В общем, <чем дальше в лес, тем больше дров> - чем больше число дистрибутивов, их версий и, вообще, программных продуктов, тем труднее запастись конкретными рекомендациями на все случаи жизни. Тем ценнее, соответственно, и знания, помогающие самостоятельно разобраться во всех возникающих проблемах.

Исходя из всего этого, мне показалось полезным изложить своё представление об этих самых стилях загрузки. Во-первых, потому что именно в ходе загрузки задаются очень многие характеристики системы. Во-вторых, потому что именно способом загрузки дистрибутивы различаются, подчас, весьма существенно. И в-третьих, потому что надо же хоть на каком-то примере показать, что традиционный setup от RedHat, одноимённый - от Slackware, всеобъемлющий linuxconf и семейство drak-конфигураторов от Mandrake модифицируют одни и те же файлы. Разнообразие средств конфигурирования перестаёт радовать, когда их количество начинает превышать число конфигурационных файлов, для <завуалированного> редактирования которых они созданы. Более того, случается, что нужный результат можно получить только <вручную>. Мне, например, пришлось однажды столкнуться с <мышами> NetScroll+, полноценную работу которых в консольном и графическом режимах удалось достичь, лишь определив РАЗНЫЕ типы мыши в /etc/sysconfig/mouse и /etc/X11/XF86Config. О настойчивых попытках запустить сетевые сервисы до активации pcmcia-устройств на ноутбуках я уже писал. Таковы были мотивы. Хочется верить, что результат кому-то пригодится. И прошу извинить за пространное вступление.

Итак, начнём с момента, когда аппаратный загрузчик уже запустил загрузчик внесистемный (GRUB, LILO), а этот последний, в свою очередь, загрузил в память ядро системы: в отсутствие основных средств ОС (они ещё попросту не загружены) компьютеру удалось <поднять себя за шнурки собственных ботинок>. Ядро инициализировалось, выполнило ряд операций, связанных с распознаванием и конфигурированием устройств, и запускает процесс init - <прародитель> всех будущих процессов. init, по <способу существования> - типичный демон. Будучи запущен однажды, он находится в памяти вплоть до остановки системы, хоть, может, и <спит> большую часть времени. С точки зрения этого самого демона, система всегда находится в камом-то определённом состоянии. Состояния эти называются уровнями выполнения. Уровнями их, в своё время, назвали потому, что система обязана была проходить их последовательно, от низшего к высшему. В принципе, это и сейчас так, хотя объяснить, почему перезагрузка выполняется на шестом, высшем уровне, я не берусь. Это - теория. Практика же состоит в том, что до момента запуска init от нас ничего не зависело (генерацию ядра и опции, которые можно было передать ему при запуске из-под LILO или GRUB, мы здесь не рассматриваем), начиная же с него, поведение системы определяется файлами конфигурации, а значит, может контролироваться. Работу самого init, например, определяет /etc/inittab. С него и начнём. Для иллюстрации BSD-стиля использован файл Slackware, для иллюстрации SysV-стиля - RedHat.

inittab
N Slackware RedHat
1 # 0 = halt
# 1 = single user mode
# 2 = unused
# 3 = multiuser mode
# 4 = X11 with KDM/GDM/XDM
# 5 = unused
# 6 = reboot
# 0 - halt
# 1 - Single user mode
# 2 - Multiuser, without NFS
# 3 - Full multiuser mode
# 4 - unused
# 5 - X11
# 6 - reboot
2 id:3:initdefault: id:3:initdefault:
3 si:S:sysinit:/etc/rc.d/rc.S si::sysinit:/etc/rc.d/rc.sysinit
4 l0:0:wait:/etc/rc.d/rc.0
su:1S:wait:/etc/rc.d/rc.K
#
rc:2345:wait:/etc/rc.d/rc.M
#
#
l6:6:wait:/etc/rc.d/rc.6
l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6
5 ca::ctrlaltdel:/sbin/shutdown -t5 -rf now
pf::powerfail:/sbin/shutdown -f +5
pg:0123456:powerokwait:/sbin/shutdown -c
ca::ctrlaltdel:/sbin/shutdown -t3 -r now
pf::powerfail:/sbin/shutdown -f -h +2
pr:12345:powerokwait:/sbin/shutdown -c
6 c1:1235:respawn:/sbin/agetty 38400 tty1
c2:1235:respawn:/sbin/qlogin /dev/tty2 root
...
1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
...
7 x1:4:wait:/etc/rc.d/rc.4
x:5:respawn:/etc/X11/prefdm -nodaemon

Вообще-то, на все основные конфигурационные файлы существуют man-страницы. Но в данном случае достаточно знать, что каждая строка файла (не считая комментариев, разумеется) имеет следующий формат:

идентификатор(метка) : уровни выполнения : действие : запускаемый процесс

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

Вторая секция определяет уровень выполнения по умолчанию. Сменив цифру в позиции <уровни выполнения> с 3 (многопользовательский консольный режим) на 4 (для Slackware) или 5 (для RedHat), вы заставите систему стартовать сразу в графическом режиме. Позиция <действие>, в данном случае, совершенно очевидна. Цифр может быть несколько; определяющей будет старшая. Пустое значение заставит init запросить уровень выполнения в ходе загрузки

Третья секция (действие - sysinit) запускает скрипт, выполняемый вне зависимости от запрошенного уровня. S (single user) в позиции "уровни выполнения" у Slackware и отсутствие значения в той же позиции у RedHat значения не имеют: в момент загрузки скрипт будет выполнен однократно в любом случае. А вот различие в именах скриптов примечательно: rc.S - первый из скриптов с именем, соответствующим соглашениям BSD-стиля - все они начинаются символами "rc.".

Активизировав виртуальную память (swap), смонтировав файловые системы, инициализировав Plug and Play устройства, загрузив модули ядра и выполнив ряд других операций, необходимых для работы на любом из уровней выполнения, init переходит к выполнению уровень-специфичных скриптов. Вызовы этих скриптов и составляют четвёртую секцию, и именно в них заключается главное отличие стилей загрузки. Для BSD-стиля, как мы видим, характерно наличие отдельного скрипта для каждого из уровней выполнения. Кроме rc.0 и rc.6, отвечающих, как нетрудно догадаться, соответственно за остановку и перезагрузку системы, скрипты rc.K и rc.M обеспечивают соответственно одно- и многопользовательский уровни выполнения. Совершенно иной алгоритм у загрузки SysV-стиля. Здесь на каждом из уровней требуется выполнение целого ряда скриптов, находящихся в подкаталогах rc0.d ... rc6.d. Подкаталоги эти содержат на самом деле лишь символические ссылки на реальные скрипты, находящиеся, в свою очередь, в подкаталоге init.d. Каждый из этих скриптов способен выполнять, как минимум, две операции - запуск и останов своего сервиса. Которая из операций будет вызвана, определяется первой буквой имени символической ссылки: "S" - для запускающих симлинков, "K" - для останавливающих. Цифры в имени определяют порядок запусков/остановов. Ух: Чем-то напоминает путь к "инфарктгенерирующей" игле Кощея Бессмертного. Но в гибкости этой системе не откажешь. Необходимость менять состав и имена симлинков случается крайне редко - обычно с этой задачей прекрасно справляется консольная ntsysv со всеми её графическими потомками. Однако случается. Как, например, в описанном случае включения на ноутбуке сетевой поддержки до инициализации pcmcia. Действие "wait" означает в данном случае необходимость для init ждать окончания запускаемого в данной строке процесса.

Строки пятой секции описывают реакцию системы на пропадание и появление питания (подразумевается сигнал от UPS и старт системы после такой остановки), и на Ctrl+Alt+Del - "салют из трёх пальцев", по выражению Патрика Фолькердинга (Patrick J. Volkerding) . По крайней мере один случай осмысленного редактирования этой строки мне известен. Заменив:

ca::ctrlaltdel:/sbin/shutdown -t3 -r now

на:

ca::ctrlaltdel:/sbin/shutdown -t3 -h now

мы заставим систему выключаться вместо перезагрузки в ответ на этот самый салют. Эффект, абсолютно бессмысленный с точки зрения "непуганых" обладателей отдельно стоящих десктопов, оказывается довольно изящным решением, когда речь идёт о корректном выключении сервера АСУТП в конце третьей смены техником Пупкиным, болезненно реагирующим на всякие login-ы с password-ами. Ну, а если сервер снабжён UPS-ом, то несложно обеспечить его корректное выключение даже рубильником.

Как вы, вероятно, догадываетесь, после выполнения скрипта из четвёртой секции система полностью готова к работе. Самое время запустить на одной или нескольких консолях (в зависимости от уровня выполнения) программу регистрации пользователя, предоставим ему, таким образом, возможность теперь уже самостоятельно общаться с системой. Что и делают строки шестой сессии. Действие respawn означает автоматический перезапуск процесса в случае его завершения. То есть: закончил сессию один пользователь - тут же появляется предложение регистрации для другого. Количество консолей определяется количеством таких строк. Какую именно программу использовать для входа пользователей в систему - дело вкуса. Во второй строке этой секции для Slackware фигурирует qlogin - скрипт, обеспечивающий, автоматическую регистрацию root на второй консоли: вопиющее нарушение правил безопасности! Но - удобно.

К седьмой секции я отнёс строки, связанные c автоматической загрузкой X11. Правый и левый скрипты не аналогичны - строка Slackware скорее должна была бы попасть в четвёртую секцию: в результате её выполнения загружается и X-сервер, и менеджер сессий, тогда как в RedHat строка из четвёртой сессии загрузит X-ы, а из седьмой - менеджер сессий. Результат, впрочем, в обоих случаях будет один и тот же.

На этом работа init приостанавливается - процесс засыпает. Поводом для <побудки> может быть <смерть> одного из запущенных процессов (действие respawn подразумевает перезапуск такого процесса), сигнал об ошибке питания (ищутся и обрабатываются строки с действиями powerfail и powerwait), <салют из трёх пальцев> (строка с ctrlaltdel) или запрос перехода на другой уровень выполнения. Таковым, в свою очередь, может быть команда выключения, перезагрузки или команда перехода в явном виде:

/sbin/init [ 0123456Ss ]

В некоторых системах имеется специальная команда <tell init>:

/sbin/telinit

Обычно это просто симлинк на init, зато очевидно, что требуется не запуск нового процесса, а передача параметра уже существующему. Запрос на изменение уровня выполнения заставляет init <перечитать> inittab, отправить всем процессам, несовместимым с запрошенным уровнем выполнения, сигнал-предупреждение SIGTERM (а если пяти секунд окажется недостаточно этим <бедолагам> для завершения, то и SIGKILL), после чего будут выполнены все строки, в поле <уровень выполнения> которых присутствует номер уровня, на который запрошен переход. И так далее. Если, конечно, запрошенный уровень не нулевой, т.е. последний для данной загрузки.

В принципе, это всё. Как именно задаются те или иные настройки системы в целом и отдельных сервисов в частности, всегда можно узнать проанализировав стартовые скрипты. Их, конечно, многовато, и требуются хотя бы минимальные представления о языке shell, на котором они преимущественно написаны, но, говорят, это вызывает затруднения только первые десять лет - потом привыкаешь. А если серьёзно, то без этих представлений все равно с Linux не поладить. Да и вряд ли потребуется редактировать многие скрипты. Возникла проблема - вооружайтесь grep, любимым редактором и ищите, начиная от rc.S или rc.sysinit, а если знаете точнее - тем лучше.

Несколько практических советов я всё же попытаюсь дать:

  • Найдите время хотя бы однажды просмотреть стартовый загрузочный скрипт (строка с действием sysinit в /etc/inittab) - именно в ходе его выполнения задаются основные настройки системы. Если Вам повезёт, то текст скрипта окажется достаточно информативным. Вышеупомянутый Патрик Волькердинг, например, умудряется цитировать в скриптах Slackware HOWTO.
  • Для RedHat-совместимых систем рекомендуется изучить состав пакета initscripts. В его состав, кроме стандартных man-ов, входит файл sysconfig.txt - подробное описание того, откуда rc.sysinit берёт параметры конфигурации.
  • Не стесняйтесь просматривать скрипты, запускающие отдельные сервисы: они, обычно, умеют выполнять не только запуск и останов <вверенных> им сервисов. Там вы увидите, какие опции составитель дистрибутива посчитал оптимальными для данного сервиса, куда записывается log и где ищутся конфигурационные файлы. Хорошо документированный скрипт может быть интереснее руководства. В том же пакете initscripts есть файл sysvinitfiles - руководство по написанию собственного скрипта инициализации. Даже если Вы не собираетесь писать такой скрипт, предлагаемая информация поможет проанализировать скрипты существующие.
  • Как я уже говорил, многие конфигурационные файлы имеют собственные man-страницы, где может быть много полезного. Вы, вероятно, удивитесь, узнав, например, что XFree86 при запуске ищет свой конфигурационный XF86Config более чем в десятке каталогов. Причём последний может ещё и именоваться по-разному.
  • Сведения, почерпнутые из скрипта, имеют <абсолютный> характер, из HOWTO, info, manual - <относительный>. Без комментариев: понятно, что скрипт, собственно, и выполняется, а информация давненько, быть может, готовилась.

К моменту запуска виртуальных консолей все основные настройки системы уже <в силе>, и я надеюсь, мне удалось убедить Вас, что посредством анализа скриптов загрузки всегда можно найти конфигурационные файлы, отвечающие за ту или иную опцию системы или сервиса. Иногда - трудно, но всегда - возможно в принципе. К сожалению, возможности конфигурирования системы на этом не исчерпываются. Само собой разумеется, что загрузка, в каком бы стиле она ни выполнялась, никак не касается конфигурации пользовательских приложений (в отличие от сервисов, симпатично по традиции именуемых обычно демонами). Здесь уже без документации не обойтись. Полезно помнить, что, как правило, приложение может иметь много конфигурационных файлов: основной - в каталоге /etc и пользовательские - в домашних каталогах пользователей. Что имена этих файлов формируются обычно из имени приложения и префиксов/суффиксов <rc>, <conf> или <config>. Что пользовательские конфигурационные файлы обычно <скрыты>, поскольку начинаются с точки (dot). Но <как правило> не означает <всегда>: сложные приложения имеют иногда целые подкаталоги конфигурирующих файлов, каталоги эти (или файлы) могут находиться в /usr/share/приложение или где-нибудь ещё, да и названия их могут выглядеть <нестандартно> и т.д. и т.п.

Ещё одной категорией конфигурационных файлов, местоположение, да и само существование которых трудно <вычислить> чисто логическим путём, являются файлы, обращение к которым происходит только по необходимости. Это, прежде всего, файлы:

  • Регламентации доступа: host.conf, hosts, hosts.allow, hosts.deny.
  • Администрирования: group, passwd, securetty, shadow.
  • Используемые при работе в сети: resolv.conf, export, networks.

Эти файлы могут использоваться и ядром, и сервисами, и отдельными приложениями. Довольно часто сервисы, кроме параметров, принятых при старте от инициализирующего скрипта, используют и собственные конфигурационные файлы. Забавно то, что о первых забывают обычно чаще, чем о вторых. Одним словом, сюрпризов в сфере конфигурации программных продуктов, используемых в Linux, - более чем достаточно. Успехов Вам в их <разборке>.