BIOS запускает POST (Power-on self test), который проверяет целостность аппаратуры; если всё в порядке, далее BIOS найдёт на каком-либо из дисков загрузочную запись MBR (Master boot record) и передаст ей управление.
MBR находится на первом секторе загрузочного диска, её объём составляет 512 байт. Она состоит из трёх компонентов:
что данный сектор является загрузочным. Это не контрольная сумма.
Формат MBR стандартен для всех операционных систем, а содержание области, отведённой под первичных загрузчик, может различаться. Перед загрузчиком стоит одна задача: найти на диске и считать в память код загрузчика следующего этапа, разворачивающего саму операционную систему.Он анализирует разделы, определяет основные и выделяет из них активный. Потом она передаёт управление PBR (Parimary Boot Record)(иногда говорят VBR) - основной загрузочной записи активного раздела. Раздел с основной загрузочной записью содержит загрузчик Linux.
MBR находится в начале жёсткого диска и содержит информацию о всех разделах на диске. PBR содержит информацию о файловой системе и выполнении процедуры загрузки для данного раздела. Может содержать (чаще всего содержит) сполняемые файлы загрузчика непосредственно операционной системы (LILO или GRUB).
Возможность интерактивного взаимодействия с пользователем, предоставляемая сложными загрузчиками операционных систем, достигается за счёт чтения файлов с диска. Загрузчик зависит от файловой системы, причём от такой, которая не оптимизирует дисковое пространство, не сжимает данные. С другими файловыми системами загрузка ядра была бы невозможна.
Время работы MBR называют первой фазой загрузки, а PBR второй. Также говорят, что MBR содержит первичный загрузчик, а PBR вторичный. Я проверил.
Программа, седержащаяся в PBR нужна для загрузки LILO или GRUB и передачи чему-то из этого управления. В то же время, принято говорить, что LILO и GRUB являются вторичными загрузчиками, хотя логично было бы назвать их загрузчиками третей фазы. Однако, если не отказываться от существующей терминологии, то придётся признать, что PBR - это часть GRUB или LILO.
Стандартный загрузчик Linux — LILO (LInux LOader) — состоит из двух частей: первичного загрузчика LI и вторичного LO. LI располагается в MBR и умеет только загружать LO, который передает управление ядру или вызывает другой первичный загрузчик (например, Windows 9х). LO находится в файле на диске (по умолчанию /boot/boot.b). О файловых системах LI не знает, поэтому карта размещения этого файла хранится в нем в виде «цилиндр/головка/сектор». помещает ее туда утилита /sbin/lilo, которую нужно запускать после любого изменения LO или его конфигурационного файла /etc/lilo.conf.
У вторичного загрузчика LO есть собственная карта размещения файлов (по умолчанию /boot/map). По ней он ищет загружаемое ядро и образ виртуального диска, поэтому после любого изменения ядра или загружаемых модулей тоже обязательно запускать утилиту lilo. Эта утилита обновляет загрузчику информацию о расположении данных на диске, которая нужна ему в весьма специфичном виде, ввиду непонимания файловых систем.
После того как ядро загружено в память и ему передано управление, оно должно смонтировать корневую файловую систему. Чтобы это сделать, ядро должно загрузить драйвер контроллера жёсткого диска, расположенный на корневой файловой системе (каталог /lib/modules). Получается замкнутый круг. Проблема решается с помощью технологии initrd (Initial RAM disk): вместе с ядром LILO загружает в оперативную(!) память образ стартового диска, и ядро монтирует его как обычную файловую систему. В этой файловой системе находятся модули, необходимые для работы со всевозможными внешними устройствами (диски в том числе) и сетью, и утилиты для их подгрузки. Подключив модули, ядро отсоединяет виртуальный диск и монтирует настоящую корневую файловую систему.
Файл образа виртуального диска обычно называется
/boot/initrd-<версия_ядра>.
Если скомпилировать ядро сразу с необходимыми модулями, отпадёт необходимость в initrd и скорость загрузки системы возрастёт.
GRUB (GRand Unified Bootloader) использует несколько отличную от LILO схему загрузки. Вторичный загрузчик хранится не в каком-то файле, а в не используемом системой пространстве. Обычно это весь остаток от первой дорожки диска. Первые 512 байт занимает MBR, 31744 байта вторичный загрузчик. Если места для полноценного вторичного загрузчика там недостаточно, туда помещается маленький загрузчик промежуточного этапа, «полуторный», состоящий из драйвера файловой системы и инструкций для вызова настоящего, большого, вторичного загрузчика. Благодаря такой организации GRUB поддерживает большинство файловых систем (FAT и FAT32, ext2fs и ext3fs, ReiserFS, XFS, BSD FFS), понимает большинство форматов исполняемых файлов и изменения в конфигурационном файле вступают в силу сразу же, без прописывания их в специальном месте специальной утилитой. Также, как и LILO, GRUB загружает в оперативную память ядро и initrd, затем передаёт управление ядру.
6. Ядро после загрузки в оперативную память ещё не в состоянии полной готовности. Загрузчик выгружает с диска только заархивированный образ ядра - vmlinuz. Он состоит из трёх частей:
vmlinuz = header + kernel setup code + vmlinuX (actual compressed kernel)
Заголовок - это часть образа ядра, в которой загрузчик операционной системы располагает параметры для его работы. Их можно найти и изменить в конфигурационных файлах загрузчика.
Как же так получается, что ядру передано управление в тот момент, когда оно в оперативной памяти представляет из себя сжатый образ самого себя?
Загрузчик передаёт управление процедуре автозапуска - части образа ядра, которая сама по себе не является сжатыми данными. Процедура автозапуска определяет объем доступной оперативной памяти, тип и тактовую частоту процессора, тип видеоадаптера и переинициализирует жесткие диски, не полагаясь на инициализацию, выполненную BIOS. Это делается для того, чтобы выбрать из возможных вариантов выполнения ядром основных функций те, которые оптимизированны именно для данной аппаратной архитектуры, повысив тем самым быстродействие всей системы. Эта же процедура определяет системную консоль, на которую выводятся диагностические сообщения.
Наконец, процедура автозапуска распаковывает загруженный в память фактический образ ядра (на этом этапе вы видите сообщение: «Uncompressing Linux…») и передает ему управление (, что иногда знаменуется сообщением «OK, booting the kernel»). Теперь ядро инициализирует таблицу страниц виртуальной памяти, устанавливает обработчики прерываний, разбирает параметры, переданные ему диспетчером загрузки, и настраивается в соответствии с ними.
Завершив самонастройку, ядро создает несколько системных «процессов», фактически представляющих собой части самого ядра (их можно увидеть в выводе команды ps): планировщик процессов, диспетчер виртуальной памяти, различные обработчики сигналов ядра.
Один из них получает идентификатор 1, он станет полноценным пользовательским процессом, в котором будет выполняться код демона init. Этот демон запустит все остальные службы и пользовательскую среду. Все программы, запускаемые пользователем, так или иначе яляются дочерними процессами от других процессов, запущенных init'ом.
Ядро монтирует корневую файловую систему сначала в режиме «только чтение», находит исполняемый файл демона init (в каталоге /bin, /sbin или там, где вы укажете, передав ядру параметр в конфигах загрузчика init=/путь_к_init) и посредством системного вызова ехес() загружает его код в процесс номер 1. Все остальные процессы порождает init и его потомки путем деления с помощью системного вызова fork().
Системный вызов exec() заменяет текущую программу новой, при этом идентификатор процесса сохраняется, а вызов fork() создаёт дочерний процесс с новым идентификатором. Этот идентификатор потомком возвращается родителю для обеспечения их связности. Таким образом выстраивается древовидная иерархия процессов.
Ядро покорно запустит в качестве первого процесса любую программу, которую вы укажете ему как init в конфигурационных файлах загрузчика:
LILO: my_linux init=/bin/sh
В данном случае вместо системы инициализации ядро запустит командную оболочку.
Процесс init читает свой конфигурационный файл /etc/inittab и запускает другие процессы согласно указанным в нем инструкциям. В этот момент выводится приглашение нажать определенную клавишу (обычно “I”), чтобы войти в интерактивный режим, позволяющий запускать каждый процесс вручную или отказываться от запуска вовсе.
В каждый момент времени система находится на одном из уровней выполнения. Уровень выполнения (runlevel) - это такой режим работы системы, в котором разрешается существование только определенной группы процессов. На каком уровне система находится в данный момент можно узнать с помощью команды who -r. Она также покажет каким был предыдущий отработавший уровень.
Разрешенные на каждом уровне процессы указаны в файле /etc/inittab. Демон init заведует переключением уровней, остановкой запрещенных процессов на новом уровне и запуском предписанных. В ОС Linux определено 7 уровней выполнения:
0 - Останов системы. 1 - Однопользовательский режим для консоли восстановления. 2 - Многопользовательский режим без поддержки Network File System. 3 - Полноценный многопользовательский режим. 4 - Не применяется. 5 - Многопользовательский режим с графической сессией X11. 6 - Перезагрузка.
init определяет уровни для выполнения по умолчанию исходя из настроек в /etc/inittab и использует его для загрузки всех необходимых программ.
Исходя из заданного уровня выполнения, система будет выполнять файлы в соответствии с нижеприведёнными директориями:
…
В каталоге /etc/ содержатся символические ссылки на вышеприведённые каталоги. Например,
/etc/rc2.d -> /etc/rc.d/rc2.d
А также есть символическая ссылка
/etc/init.d -> /etc/rc.d/init.d
Не спрашивайте зачем все эти ссылки в каталоге /etc/.
В каталоге /etc/rc.d/init.d содержатся сценарии и иногда бинарные файлы, соответствующие какому-либо запускаемому демону.
В каталогах /etc/rc.d/rc*.d/ находится символические ссылки, указывающие на какой-либо демон, находящийся в /etc/rc.d/init.d. В последнем названном каталоге находятся вообще все демоны, запускаемые на каком либо из уровней. Когда init'у предписано воспроизвести, например, уровень 5, он будет исполнять только те демоны, содержащиеся в /etc/rc.d/init.d, на которые есть символические ссылки в каталоге /etc/rc.d/rc5.d.
Ссылки, которые начинаются с “S” используются для запуска (start), а те, которые начинаются с “K” для завершения (kill). После этих букв идут номера, которые используются для определения очерёдности запуска программ.
Каждый из настоящих сценариев, будучи вызван с аргументом start, запускает свой демон, а с аргументом stop останавливает ее. Какой аргумент будет ему передан, зависит от первой буквы имени символической ссылки: если S, start; если К, stop.
Сначала выполняются все сценарии завершения работы демонов, соответствующие символическим ссылкам с буквой K - эти демоны могли быть запущены на другом уровне ранее - потом выполняются все сценарии запуска, соответствующие символическим ссылкам с буквой S.
Для наглядности сравните между собой каталоги /etc/rc.d/rc5.d и /etc/rc.d/rc0.d.
Термин «уровень выполнения» унаследован с тех времен, когда система была обязана проходить уровни последовательно, от низшего к высшему при загрузке и обратно при выключении. Сейчас их можно переключать в любом порядке. Для переключения на уровень n нужно от имени суперпользователя ввести команду
# telinit n
Эта команда посылает соответствующий сигнал процессу init. Ее исполняемый файл представляет собой символическую ссылку на /sbin/init, так что вместо нее можно писать команду init, при этом не будет запущена копия процесса init: стартующий процесс первым делом проверяет свой PID и, если тот не равен 1 то он, просто передает сообщение настоящему процессу init.
Переключением уровней выполнения занимается не init как таковой (/sbin/init), а его центральный сценарий - /etc/гс.d/rc.
Команда init (или telinit) n, где n - номер включаемого уровня, запускает сценарий /etc/гс.d/rc с аргументом n. Этот сценарий выполняет в каталоге /etc/rcN.d сначала все стоп-сценарии, потом все старт-сценарии.
Запустив все процессы, приписанные к текущему уровню выполнения, init засыпает до получения сигнала:
При получении какого-либо из них, init просыпается, перечитывает свой конфигурационный файл и, если нужно, выполняет записанные в нем инструкции. Чтобы заставить init перечитать измененный вами /etc/inittab, не дожидаясь трех вышеуказанных событий, введите команду
# telinit q
Самое основное в этой части можно резюмировать следующим образом: чтобы обеспечить автоматический запуск какого-нибудь демона, нужно создать сценарий для его запуска и поместить его в каталог /etc/init.d. Затем, в зависимости от уровня выполнения, в каталоге /etc/rcN.d нужно создать символические ссылки на этот сценарий для его запуска и завершения.
Выполнив все сценарии, init переходит к другим записям в /etc/inittab, относящимся к текущему уровню. Обычно там остаются только перезапускаемые (respawn) действия. Мы их ещё рассмотрим. Инициализация системы считается законченной, когда запущены все перезапускаемые процесс и init остается только следить за ними.
Первая незакомментированная строка файла конфигурационного файла /etc/inittab определяет уровень выполнения по умолчанию, то есть тот, в котором стартует система, если в процессе загрузки ядру не указано иное. Эта строка выглядит как
id:5:initdefault
Обычно в качестве уровня по умолчанию выбирают 3 (полнофункциональный многопользовательский текстовый режим) или графический 5 (запускается X Window и выдается графическое приглашение для входа в систему). Если оставить поле уровня пустым, то init переспросит значение в процессе загрузки. Если указать в поле уровня несколько значений, то сработает наибольшее. Уровни 0 (останов) и 6 (перезагрузка) указывать нельзя.