Введение в POSIX'ивизм

       

Задачи инициализации


Итак, тем или иным способом загрузка ядра и всего сопутствующего ему хозяйства успешно завершена. В дело вступает главный калибр любой POSIX-системы - процесс init. Это первый (в прямом и переносном смысле его PID равен единице) пользовательский (то есть работающий в пользовательском пространстве ядра, юзерланде, процесс, и запускается он исполнением одноименного файла /sbin/init.

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

Первой из таких задач, как по времени исполнения, так и по значению, является проверка целостности наличных файловых систем. Для начала каждая из них проверяется на наличие бита "чистого размонтирования" (clean byte), который автоматически устанавливается в ходе правильного завершения предыдущего сеанса работы. Если такой бит обнаруживается на каждой файловой системе - все хорошо, дело движется дальше. Если нет - возможны варианты, о которых я скажу позднее.

Следует отметить, что сам по себе "бит чистого размонтирования" отнюдь не гарантирует сохранности файловой системы и, особенно, ее данных. Он лишь показывает, что файловая система была корректно размонтирована в предыдущем сеансе. В этом случае процесс init делает не лишенное резона допущение, что с метаданными и данными ее все в порядке, и переходит к выполнению следующей задачи.

А следующая задача процесса init - это вызов и отработка сценариев инициализации, или стартовых скриптов, собранных в каталоге /etc и (или) его подкаталогах. Они столь сильно зависят от операционки (а внутри Linux - еще и от конкретного дистрибутива), что дать их обобщенную характеристику практически невозможно. Можно только констатировать, что сценарии инициализации - это обычные сценарии оболочки, рассчитанные на исполнение стандартным POSIX-шеллом (/bin/sh в BSD-системах и, обычно, /bin/bash - в Linux).
Они включают в себя последовательности команд, призванные монтировать файловые системы, активизировать область своппинга, устанавливать системные часы, запускать те или иные службы и демоны, включая сетевые соединения.
Команды, образующие стартовые сценарии, получают свои опции, их значения и аргументы из специальных файлов конфигурации, также имеющих своим местопребыванием /etc и его подкаталоги. Конфигурационные файлы (или по простому конфиги) представляют собой либо простые базы данных опций и аргументов команд, либо списки имен переменных, (соответствующих опциям команд, используемых в скриптах) с присвоенными им значениями. Конфиги от скриптов легко отличимы при просмотре каталога /etc отсутствием у первых бита исполнения.
Сказанное может показаться не очень понятным, поэтому попытаюсь продемонстрировать на примере. Например, обязательная процедура на стадии отработки сценариев инициализации - монтирование необходимых файловых систем в режиме чтения/записи; и перемонтирование - ведь при загрузке ядра несущая его корневая файловая система монтируется в режиме "только для чтения". Это выполняется прямой директивой
$ mount -a
предписывающей смонтировать все файловые системы, и входящей в состав одного из стартовых сценариев (каком именно - зависит от оперционки и дистрибутива). А вот что понимается под словом "все" (имя опции -a - от all) - то есть список аргументов (устройств и точек монтирования), а также опций, с которыми должна быть смонтирована та или иная файловая система, - и составляет содержание специальной базы данных, хранимой в файле /etc/fstab. Мы уже знакомились с ним в , однако позволю себе напомнить обобщенный его формат (т ем паче, что это один из немногих конфигов, формат которого идентичен во всех POSIX-системах).
Это - очень простая база данных, каждая запись которой соответствует подлежащей монтированию файловой системе, а поля, разделителем которых являются символы пробела (пробелов) или табуляции, следующие:
имя файла устройства, несущего файловую систему; точка монтирования - каталог в файловой иерархии; тип файловой системы; опции монтирования (часто имеет значение default).




Содержимое первых двух полей каждой записи передается команде mount из стартового сценария в качестве первого и второго ее аргументов, остальных двух - как опции, обязательные (тип файловой системы) и необязательные (все прочие).
Последовательное разделение стартовых сценариев и их конфигурационных файлов - один из краеугольных принципов общесистемного конфигурирования. В сущности, пользователю при нормальном ходе настройки практически нет необходимости ни знакомиться с содержимым скриптов (хотя это и не вредно), ни, тем паче, менять в них что-либо (последнее допустимо только в том случае, если этот самый пользователь точно знает, что делает, иначе систему легко довести до неработоспособного состояния). А вот вносить изменения в значения параметров конфигурационных файлов - не только можно, но и нужно - разумеется, такое разрешение не избавляет пользователя от понимания смысла своих действий.
Наконец, третья непременная задача процесса init - теч, что в главе 7 мы рассмотрели как получение терминала (запуск процесса getty), установку его свойств и подготовку к авторизации - вытеснение его процессом login. Эта процедура также по разному выполняется в разных системах, хотя тут многообразие не столь и велико, как при отработке стартовых скриптов.
В ходе инициализации могут выполняться и некоторые другие задачи, скажем, конфигурирование приложений, не входящих в базовую систему, но, тем не менее, запускаемых в качестве стартовых сервисов (демонов). Поскольку такие программы устанавливаются отдельно от системы (и не в обязательном порядке), сценарии их запуска и сопряженные с ними конфиги лежат не в каталоге /etc, а в других, подчас весьма неожиданных, местах (/usr/etc, /usr/local/etc и так далее). Типичным примером здесь является httpd - демон, управляющий web-сервером Apache. Однако все это - уже не обязательные составляющие этапа инициализации.
Описанная последовательность инициализации происходит при бессбойном ее протекании - если ни на одной из стадий не происходит ошибок.


Если же таковые случаются - по причине аппаратных ли сбоев или ошибок пользователя при настройке, - все может протекать совершенно иначе.
Первый потенциальный источник ошибок инициализации - отсутствие "бита чистого размонтирования", выявляемое при начальной проверке файловых систем. В этом случае делается вывод о некорректности завершения предыдущего сеанса и возможности существования противоречий в ней. Таковыми могут быть, например, записи в каталогах, которым не соответствуют идентификаторы каких-либо файлов. Или, напротив, идентификаторов файлов, не приписанных ни к каким каталогам.
Для проверки, действительно ли такие противоречия имеют место быть (а отсутствие clean byte отнюдь не влечет их неизбежности) и, при необходимости, исправления выявленного безобразия, запускается утилита проверки файловой системы fsck (это разные программы, в зависимости от типа проверяемых файловых систем). Если такая проверка завершается успешно - то есть противоречий в структуре файловой системы на самом деле нет или могут быть исправлены автоматически - опять же все хорошо, процесс init возвращается к выполнению своих задач, если нет - снова возможны варианты, зависящие от ОС и "тяжести повреждений". Худший случай - когда серьезные противоречия обнаруживаются в корневой файловой системе, это знаменует окончание нормальной инициализации с переходом в однопользовательский режим, когда монтируется только корневая файловая система - и в режиме read only.
Ошибки при отработке скриптов инициализации также влекут разные последствия. Обычный (и наиболее легкий) случай - что сервис, в сценарии запуска которого произошла ошибка (а она может быть вызвана, например, неправильны указанием опций или аргументов в соответствующем конфиге), просто не будет доступным после загрузки системы. Например, так будет с демоном консольной мыши в Linux - gpm, если в его конфиге неправильно был указан протокол или интерфейс мыши.
Более серьезные последствия будут иметь ошибки при монтировании файловых систем, самый частый источник которых - синтаксически неправильное их описание в файле /etc/fstab (то есть элементарные опечатки).


Особенно серьезным будет невозможность монтирования корня файлового древа - этом случае ядро впадет в панику (т.н. Panic mode) и продолжение загрузки окажется невозможным.
Наконец, ошибки в процессе получения терминала (возможные при экспериментах, например, с автоматической регистрацией в системе). Они также достаточно неприятны, и, как правило, приводят к невозможности начала нормальной работы.
Впрочем, оснований для паники нет - даже при впадении в панику ядра, все возможные при инициации системы ошибки исправимы тем или иным способом. Ибо легкий флирт, в том числе и с операционной системой, подобно насморку, переносится на ногах, и постельный режим необходим лишь в тяжелых случаях. В одних ситуациях достаточно завершить процесс загрузки и чуть подправить конфиги, в других - загрузиться в однопользовательском режиме и провести проверку и ремонтирование файловых систем вручную, в третьих придется грузиться с rescue-носителя (например, LiveCD). А вот хирургического вмешательства - сиречь полной переустановки системы, - скорее всего, не потребуется никогда. Впрочем, все это будет предметом отдельного разговора в одной из ближайших интермедий.
Оборотная сторона инициализации системы - это ее останов или рестарт, различий между этими процессами практически нет. И отвечает за него команда shutdown, которая может быть дана от лица суперпользователя или члена группы operator. С опцией -h она вызывает останов машины, с опцией -r - ее перезагрузку. И еще команде этой требуется аргумент - время, когда останов или рестарт должны произойти. Впрочем, есть способ и мгновенного останова или рестарта:
$ shutdown -h now
или
$ shutdown -r now
соответственно.
Во всех, насколько мне известно, POSIX-системах существуют также команды halt и reboot того же назначения. Однако самостоятельной роли они не играют, просто вызывая команду shutdown с опцией останова и перезагрузки, соответственно.
Останов системы происходит в порядке, обратном ее инициализации. Сначала делается попытка корректного завершения всех пользовательских процессов отправкой им сигнала TERM.


По истечении некоторого промежутка времени всем еще "живым" процессам отправляется сигнал KILL - для гарантированного их убиения. Затем стопорятся все стартовые сервисы и демоны. Наконец, содержимое дисковых кэшей записывается на диск (посредством команды sync), и размонтируются файловые системы. После этого обычно появляется сообщение о возможности безопасного отключения питания машины или оно отключается автоматически. При рестарте все происходит точно также, но после останова системы автоматически начинается перезагрузка машины. Весь процесс останова и (или) рестарта определяется соответствующим сценарием (или сценариями), подобными сценариям инициализации.
Важно - и это одно из первых правил техники безопасности, - что Unix-машину крайне не рекомендуется останавливать простым отключением питания. Каковое чревато разными неприятностями. Это могут быть
  • потери данных в запущенных программа: не случайно первое действие при останове - попытка их сохранения сигналом TERM;

  • потери как бы сохраненных, но еще не записанных (то есть кэшированных) изменений в открытых файлах;

  • отсутствие бита чистого размонтирования в нежурналирумых файловых системах, вызывающее их более или менее длительную проверку при рестарте;

  • более или менее серьезные нарушения целостности файловых систем, вплоть до полного их разрушения (впрочем, последнее нынче случается крайне редко.

  • В современных версиях POSIX-систем в той или иной мере реализована поддержка управления питанием стандарта ACPI (Advanced Configuration and Power Interface). При ее включении в принципе становится допустим останов системы простым отключением питания машины - на нажатие кнопки Power на корпусе компьютера система реагирует точно так же, как и на команду shutdown -h now. Однако прибегать к этому следует только в случае уверенности в правильности настройки и функционирования модулей acpi - и в Linux, и в BSD-системах эти опции экспериментальны, и их безошибочная работа не гарантируется.

    Содержание раздела