Linux — сложная операционная система, и процедура ее включения/выключения не сводится к простому нажатию кнопки питания. Следовательно, чтобы система работала корректно, выполняйте операции запуска и останова по всем правилам.
Хотя процесс начальной загрузки системы всегда был достаточно сложным, все же он был несколько проще в те дни, когда изготовители определяли буквально все аспекты аппаратного и программного обеспечения. Теперь, когда Linux управляет аппаратным обеспечением персональных компьютеров (ПК), процедура загрузки должна выполняться по правилам ПК. При этом приходится иметь дело с множеством возможных конфигураций.
Под начальной загрузкой Linux понимается запуск системы при включении питания. Поскольку обычные средства операционной системы на данном этапе еще недоступны, система должна в буквальном смысле “обслужить себя самостоятельно”. В ходе этого процесса ядро системы загружается в память и активизируется. Затем выполняется ряд инициализационных задач, после чего система готова к обслуживанию пользователей.
Начальная загрузка — это период особой уязвимости системы. Ошибки в конфигурационных файлах, сбои в работе или отсутствие нужного оборудования, повреждения файловых систем могут помешать компьютеру нормально начать работу. Настройка режимов загрузки часто является одной из первых задач, которую приходится решать администратору в новой системе. К несчастью, эта задача — одна из наиболее сложных, и для ее решения необходимо хорошо знать Linux.
Когда включается питание, запускается на выполнение загрузочный код, хранящийся в ПЗУ. Он должен запустить ядро. Ядро опрашивает состояние аппаратных устройств, а затем запускает демон init
, идентификатор которого всегда равен 1.
Прежде чем на экране появится регистрационное приглашение, должен произойти целый ряд событий. Файловые системы должны быть проверены и смонтированы, а системные демоны — запущены. Соответствующие процедуры реализуются с помощью сценариев интерпретатора команд, которые последовательно запускаются демоном init. Эти сценарии часто называют rc-файлами, поскольку они имеют префикс “rc
”. Он расшифровывается как “run command” (команда запуска) и является пережитком, оставшимся в наследство от операционной системы CTSS, существовавшей примерно в 1965 году. Конкретная структура сценариев и способ их выполнения зависят от системы.
Автоматическая и ручная загрузка
Linux-системы могут загружаться автоматически либо вручную. В первом случае система запускается самостоятельно, без какого-либо вмешательства извне. Во втором случае также все происходит автоматически до определенного момента, а перед выполнением основных инициализирующих сценариев управление передается оператору (человеку, сидящему за терминалом). В этот момент система входит в так называемый “однопользовательский режим”. Большинство системных процессов еще не начало выполняться, и вход других пользователей в систему невозможен.
В повседневной работе почти всегда применяется автоматическая загрузка. Типичная процедура загрузки на современном компьютере выглядит так: пользователь включает питание и ждет (ждет, ждет, ждет...), пока система не перейдет в диалоговый режим. Системный администратор, однако, обязан не только понимать, как проходит автоматическая загрузка, но и знать, как загрузить систему вручную. К последнему средству приходится прибегать при возникновении проблем, делающих невозможной автоматическую загрузку. Это может быть вызвано, например, повреждением файловой системы или ошибками в конфигурации сетевой платы.
Этапы загрузки
Типичная процедура начальной загрузки системы Linux состоит из шести этапов:
- загрузка и инициализация ядра;
- обнаружение и конфигурирование устройств;
- создание процессов ядра;
- действия оператора (только при ручной загрузке)
- выполнение сценариев запуска;
- работа в многопользовательском режиме.
Почти все этапы не требуют контроля со стороны администратора. Можно управлять загрузкой системы, редактируя сценарии запуска.
Инициализация ядра
Ядро Linux представляет собой программу, и первым этапом начальной загрузки является запись этой программы в память для последующего выполнения. Файл ядра обычно называется /vmlinuz
или /boot/vmlinuz
.
В Linux загрузка ядра осуществляется в два этапа. Сначала в память компьютера с диска считывается (с помощью кода, записанного в ПЗУ) небольшая программа начальной загрузки, которая затем выполняет собственно загрузку ядра.
Ядро выполняет тесты, позволяющие определить, сколько памяти имеется в распоряжении системы. Часть внутренних структур ядра имеет фиксированный размер, поэтому ядро резервирует определенный объем физической памяти для самого себя. Эта память недоступна пользовательским процессам. Ядро выдает на консоль сообщение об общем объеме физической памяти и объеме памяти, доступной пользователю.
Конфигурирование аппаратных средств
Одна из первых задач ядра — выявление компонентов аппаратного обеспечения. Создавая ядро для конкретной системы, можно указать, какие устройства оно должно проверять. При активизации ядро пытается найти и проинициализировать все устройства, о которых ему было сообщено. Ядро выводит на консоль краткую информацию о каждом обнаруженном устройстве. Современные дистрибутивы включают ядро, которое способно работать в большинстве аппаратных конфигураций компьютеров, требуя при этом минимальной настройки или же вообще обходясь без нее.
Информация об устройствах, задаваемая при конфигурировании ядра, зачастую оказывается неполной. В подобной ситуации ядро пытается получить необходимые сведения, опрашивая устройства, подключенные к системной шине, и собирая нужную информацию у соответствующих драйверов. Драйверы отсутствующих или не отвечающих на контрольный сигнал устройств отключаются. Если позднее устройство будет подключено к системе, можно будет динамически загрузить или активизировать его драйвер.
Процессы ядра
После завершения этапа базовой инициализации ядро создает в области памяти, выделенной для пользовательских программ, несколько “самовыполняющихся” процессов. Это происходит “в обход” стандартного системного вызова fork
.
Количество и характер таких процессов зависят от операционной системы. В Linux это демон init
(всегда имеет идентификатор 1; процесс с идентификатором 0 отсутствует) и различные обработчики памяти и сигналов ядра, в том числе те, которые представлены в табл. 1. Все эти процессы имеют идентификаторы с низкими номерами, а их имена в списках ps
заключены в квадратные скобки (например, [kacpid]
). Иногда имена процессов могут содержать в конце символ косой черты и цифру, как, например, [kblockd/0]
. Число указывает процессор, на котором выполняется данный процесс. Эта информация может быть полезна при настройке многопроцессорной системы.
Таблица 1. Некоторые наиболее часто встречающиеся процессы ядра Linux
Процесс | Назначение |
kjournald | Записывает обновления журнала ext3 на диск |
kswapd | Выполняет подкачку процессов при недостаточном объеме физической памяти |
kreclaimd | Освобождает страницы памяти, которые давно не использовались |
ksoftirqd | Выполняет обработку нескольких уровней мягких прерываний |
khubd | Выполняет конфигурирование USB-устройств |
Из этих процессов только init
является полноценным пользовательским процессом; остальные фактически представляют собой части ядра, которые были сделаны процессами из концептуальных соображений.
После создания перечисленных процессов ядро больше не принимает участия в процедуре начальной загрузки системы. К этому моменту, однако, еще не создан ни один из процессов, управляющих базовыми операциями (например, регистрацией пользователей в системе), и большинство демонов не запущено. Обо всем этом позаботится (в некоторых случаях косвенно) демон init
.
Действия оператора (только при ручной загрузке)
Если систему нужно запустить в однопользовательском режиме, оператор устанавливает в командной строке специальный флаг (слово “single
”), а ядро передает эту информацию демону init
. Последний, в свою очередь, передает управление команде sulogin
— специальной версии команды login
, — которая выдает приглашение ввести пароль пользователя root. Если он введен правильно, запускается интерпретатор команд с правами суперпользователя. Можно не задавать пароль, а просто нажать <Ctrl+D
>, после чего загрузка продолжится в многопользовательском режиме.
В однопользовательском режиме команды выполняются почти так же, как и в полностью загруженной системе. Однако в SUSE, Debian и Ubuntu обычно монтируется только корневой раздел. Чтобы можно было использовать программы, находящиеся вне каталогов /bin
, /sbin
или /etc
, необходимо вручную смонтировать остальные файловые системы.
Во многих однопользовательских средах корневой каталог файловой системы монтируется в режиме только для чтения. Если /tmp
является частью корневой файловой системы, выполнение множества команд, которые используют временные файлы (таких как vi
), будет невозможно. Чтобы выйти из положения, придется начать однопользовательский сеанс с повторного монтирования каталога /
в режиме чтения/записи. Нужное действие выполняет команда:
# mount -o rw,remount /
В системах Red Hat и Fedora загрузка в однопользовательском режиме выполняется несколько более активно, нежели обычно. До того момента, когда на экран будет выведено приглашение интерпретатора команд, эти дистрибутивы попытаются смонтировать все локальные файловые системы. Хотя на первый взгляд такой подход кажется удобным, но при использовании недостаточно продуманной файловой системы он может порождать проблемы.
Команда fsck
, которая проверяет и восстанавливает поврежденные файловые системы, обычно выполняется в ходе автоматической загрузки. Если система запускается в однопользовательском режиме, команду fsck
придется вводить вручную.
Когда интерпретатор команд однопользовательского режима завершит свою работу, система продолжит загрузку в многопользовательском режиме.
Выполнение сценариев запуска системы
В тот момент, когда система сможет выполнять сценарии запуска, ее уже можно назвать Linux. Это еще не полностью загруженная система, но “загадочных” этапов процесса загрузки больше не осталось. Файлы сценариев представляют собой обычные командные файлы, которые выбираются и запускаются демоном init по сложному, но, в общем-то, понятному алгоритму.
Точное местонахождение, содержимое и организация сценариев запуска заслуживают отдельного изучения.
Работа в многопользовательском режиме
После выполнения сценариев запуска система полностью готова к работе, за одним исключением: никто не может в нее войти. Для того чтобы с конкретного терминала (включая системную консоль) можно было зарегистрироваться в системе, должен быть запущен процесс getty
, ожидающий поступления запросов от этого терминала. Демон init
порождает все необходимые процессы getty
, заканчивая этап начальной загрузки. Если система сконфигурирована для работы в графическом режиме, демон init также порождает соответствующие регистрационные процессы, такие как xdm или gdm.
Не забывайте о том, что демон init
продолжает играть важную роль даже по завершении начальной загрузки. У него есть один однопользовательский и несколько многопользовательских “уровней выполнения”, определяющих, какие ресурсы системы будут доступны пользователю.
Загрузка Linux на персональном компьютере
До этого момента описывалась общая схема начальной загрузки. Теперь некоторые наиболее важные (и сложные) ее этапы необходимо рассмотреть подробнее, проанализировав дополнительные особенности функционирования персональных компьютеров.
Загрузка системы на персональном компьютере — это многоступенчатый процесс. Когда включается компьютер, начинает выполняться код, записанный в ПЗУ. Точное его местонахождение и структура зависят от типа оборудования. В компьютерах, созданных специально для UNIX или другой коммерческой операционной системы, код “прошивается” разработчиком, который заранее задает алгоритм подключения устройств, базовой инициализации сети и распознавания локальных файловых систем. Это очень удобно для системного администратора. Ему достаточно ввести лишь имя нового файла ядра, а код ПЗУ автоматически обнаружит и прочитает этот файл.
На персональных компьютерах код начальной загрузки представлен в виде базовой подсистемы ввода-вывода — BIOS (Basic Input/Output System), которая чрезвычайно упрощена в сравнении с фирменным кодом UNIX-станций. В действительности в BIOS есть несколько уровней кода: для самого компьютера, для видеоплаты, для SCSI-адаптера, если таковой имеется, и, иногда, для других периферийных устройств вроде сетевых плат.
Встроенному коду BIOS известно об устройствах, расположенных на материнской плате, в частности контроллере IDE (и жестких дисках), плате сетевого адаптера, контроллере клавиатуры, последовательных и параллельных портах. SCSI-адаптеры знают только об устройствах, подключенных непосредственно к ним. К счастью, за последние несколько лет сложные взаимодействия, необходимые для обеспечения совместной работы этих устройств, были стандартизованы и теперь почти не требуют вмешательства оператора.
В современных компьютерах код BIOS интеллектуальнее, нежели раньше. Они позволяют на этапе начальной загрузки входить в режим конфигурирования, удерживая нажатой одну или две клавиши. В большинстве случаев названия этих клавиш отображаются на экране, чтобы их не нужно было искать в документации.
В режиме конфигурирования можно выбрать, с какого устройства требуется загружаться, хотя выбор не так уж велик. Обычно последовательность загрузки задается в виде правила, например: “сначала — дисковод для гибких дисков, затем — дисковод CD-ROM, в последнюю очередь — жесткий диск”. К сожалению, в некоторых BIOS загрузка ограничена возможностью загрузки с первого IDE-дисковода CD-ROM или первого жесткого диска IDE. Могут также распознаваться SCSI-адаптеры.
Когда компьютер определил, с какого устройства следует загружаться, считываются первые 512 байтов с диска. Этот сегмент диска называется главной загрузочной записью (ГЗЗ). В ней хранится программа, которая сообщает компьютеру о том, в каком разделе диска расположена программа вторичной загрузки (загрузчик операционной системы).
Стандартная программа, находящаяся в ГЗЗ, дает компьютеру указание извлечь загрузчик ОС из первого раздела диска. В Linux поддерживаются более сложные программы, которые могут работать с несколькими операционными системами и ядрами.
Найдя раздел, с которого будет выполняться загрузка системы, программа ГЗЗ пытается запустить загрузочную программу, связанную с этим разделом. В случае успеха этой программе передаются полномочия по дальнейшей загрузке ядра.