Подробности процесса загрузки Linux

Путешествие от Master Boot Record до первого приложения, исполняющегося в пространстве пользователя

Когда-то давно термин bootstrapping (загрузка) в компьютерной области означал загрузку бумажной ленты, на которой хранилась программа начальной загрузки, или же ввод программы начальной загрузки вручную при помощи расположенных на передней панели переключателей адреса/данных/управления. Современные компьютеры оборудованы устройствами, которые значительно упрощают процесс первоначальной загрузки – однако это не означает, что этот процесс является простым.

Давайте сначала бросим самый общий взгляд на процесс начальной загрузки Linux, чтобы охватить картину полностью. Затем мы более подробно рассмотрим, что происходит на каждом этапе процесса. Ссылки на исходный код, которые будут приводиться постоянно в процессе изложения, помогут при изучении дерева исходных кодов ядра и подскажут, где получить дополнительную информацию.

Общие сведения

На рисунке 1 показан вид с «высоты птичьего полета».
Рисунок 1. Вид на процесс начальной загрузки в Linux с высоты птичьего полета
Взгляд на загрузку ядра Linux с самого высокого уровня

При первоначальной загрузке системы или при ее перезагрузке системы процессор выполняет код, который расположен в хорошо известном месте. В персональном компьютере это место соответствует базовой системе ввода/вывода (BIOS), которая хранится в расположенной на системной плате микросхеме энергонезависимой flash-памяти. Центральный процессор (CPU) встраиваемой системы обращается к reset-вектору для получения адреса программы, которая хранится по известному адресу в flash/ROM-памяти. В любом случае это приводит к одному и тому же результату. Так как персональные компьютеры отличаются намного большей универсальностью, BIOS должна определить, какие именно устройства являются кандидатами на выполнение начальной загрузки. Мы подробно рассмотрим данный процесс позже.

После того, как устройство, с которого будет осуществляться начальная загрузка, найдено, начальный загрузчик первой ступени загружается в оперативную память и начинается его выполнение. Этот начальный загрузчик имеет размер менее 512 байт (один сектор), и его задачей является загрузка начального загрузчика второй ступени.

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

init

) и затем выполняется инициализация системы высокого уровня.

Таков в общих чертах процесс начальной загрузки в Linux. Теперь давайте углубимся чуть далее и рассмотрим некоторые подробности начальной загрузки в Linux.

В начало

Запуск системы

Запуск системы определяется той аппаратной платформой, на которой выполняется начальная загрузка Linux. На встраиваемых платформах при включении питания системы или при перезагрузке используется bootstrap-среда. В качестве примеров можно привести U-Boot, RedBoot и MicroMonitor от компании Lucent. Эти программы хранятся в специальной области flash-памяти, расположенной во встраиваемой системе: они предоставляют средства для загрузки образа ядра Linux во flash-память и в дальнейшем обеспечивают выполнение этого ядра. В дополнение к функциям по хранению и загрузке образа Linux эти мониторы загрузки также выполняют тестирование системы на каком-то уровне и инициализацию аппаратного обеспечения. Во встраиваемых системах подобные мониторы загрузки обычно объединяют функции загрузчиков первой и второй ступени.

Просмотр содержимого MBR

Для просмотра содержимого MBR используйте следующую команду:

 # <strong>dd if=/dev/hda of=mbr.bin bs=512 count=1</strong>
# <strong>od -xa mbr.bin</strong>

Команда

dd

, которая выполняется с правами root, считывает первые 512 байт с /dev/hda (первый IDE-жесткий диск) и записывает их в файл

mbr.bin

. Команда

od

выполняет печать полученного двоичного файла в форматах hex и ASCII.

На персональных компьютерах загрузка Linux начинается в BIOS с адреса 0xFFFF0. Первым действием, которое выполняет BIOS, является тестирование при включении питания (POST, power-on self test). Задачей такого тестирования является выполнение проверки аппаратного обеспечения. Вторым действием POST является выполнение нумерации и инициализации для локальных устройств.

Если учитывать различное назначение функций BIOS, то можно считать, что BIOS состоит из двух частей: кода POST и сервисов времени выполнения. После завершения POST соответствующий код удаляется из памяти, однако сервисы времени выполнения BIOS остаются в памяти и доступны для операционной системы.

Для выполнения загрузки операционной системы сервисы времени выполнения BIOS выполняют поиск таких устройств, которые являются активными и способны выполнять загрузку – причем поиск выполняется в порядке, который определяется настройками, сохраненными в CMOS-памяти. В качестве загрузочных устройств могут выступать флоппи-диски, CD-ROM, разделы на жестком диске, подключенное к сети устройство или даже портативный USB-накопитель.

Обычно загрузка Linux производится с жесткого диска, на котором в MBR содержится первичный начальный загрузчик. MBR представляет собой сектор размером 512 байт, который располагается в первом секторе диска (сектор 1 цилиндра 0, головка 0). После того как MBR загружается в память, BIOS передает ему управление.

В начало

Загрузчик 1-й ступени

Первичный начальный загрузчик, хранящийся в MBR, представляет собой образ размером 512 байт, который содержит как программный код, так и небольшую таблицу разделов (см. рисунок 2). Первые 446 байт представляют собой собственно первичный загрузчик, который содержит как программный код, так и текст сообщений об ошибках. Следующие 64 байта представляют собой таблицу разделов, которая содержит запись для каждого из четырех разделов диска (по 16 байт каждая). В конце MBR располагаются два байта, которые носят название «магического числа» (0xAA55). Это магическое число служит для целей проверки MBR.
Рисунок 2. Строение MBR
Строение MBR

Задача первичного загрузчика — отыскать и загрузить вторичный загрузчик (загрузчик второй ступени). Он делает это, просматривая таблицу разделов в поиске активного раздела. Когда первичный загрузчик обнаруживает активный раздел, он просматривает оставшиеся разделы с целью убедиться, что они не являются активными. После завершения этой проверки с устройства в оперативную память считывается загрузочная запись активного раздела.

В начало

Загрузчик 2-й ступени

Вторичный загрузчик или загрузчик второй ступени было бы более логично назвать загрузчиком ядра. Его задачей на данной стадии является загрузка ядра Linux и, возможно, загрузка начального RAM-диска.

Загрузчики GRUB разных ступеней

Директория

/boot/grub

содержит загрузчики

stage1

,

stage1.5

и

stage2

, а также некоторые альтернативные загрузчики (например, CR-ROM использует

iso9660_stage_1_5

).

Загрузчики для среды x86, которые объединяют в себе загрузчики первой и второй стадии, носят название Linux Loader (LILO) или GRand Unified Bootloader (GRUB). Так как LILO имел некоторые недостатки, которые были исправлены в GRUB, то далее мы будем рассматривать именно GRUB. (Большое количество дополнительных ссылок на материалы по GRUB, LILO и связанным с ними темам содержится в разделе Ресурса в конце данной статьи.)

Одним из наибольших достоинств GRUB является то, что он способен понимать используемые в Linux файловые системы. Вместо того чтобы подобно LILO, обращаться непосредственно к секторам жесткого диска, загрузчик GRUB способен загружать ядро Linux с файловых систем ext2 или ext3. Это достигается благодаря превращению двухступенчатого загрузчика в трехступенчатый. Ступень 1 (MBR) загружает загрузчик 1.5-ступени, способный понимать файловую систему, в которой хранится образ ядра Linux. Примерами могут служить

reiserfs_stage1_5

(для загрузки из файловой системы с журналированием Reiser) или

e2fs_stage1_5

(для загрузки из файловых систем ext2 или ext3). После того, как загрузчик 1.5 ступени загружен и выполняется, может быть загружен загрузчик 2-й ступени.

После загрузки 2 ступени GRUB способен по запросу показать список имеющихся ядер (которые определяются в

/etc/grub.conf

, с поддержкой мягких ссылок из

/etc/grub/menu.lst

и

/etc/grub.conf

). Вы можете выбрать нужное ядро и даже передать ему дополнительные параметры ядра. Также существует возможность воспользоваться оболочкой с поддержкой командной строки, что обеспечивает большую степень контроля над процессом загрузки.

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

initrd

. Когда эти образы готовы к работе, загрузчик 2-й стадии вызывает образ ядра.

В начало

Ядро

Ручная загрузка в GRUB

Из командной строки в GRUB можно загрузить нужное ядро с указанным образом

initrd

следующим образом:

grub> kernel /bzImage-2.6.14.2
   [Linux-bzImage, setup=0x1400, size=0x29672e]

grub> initrd /initrd-2.6.14.2.img
   [Linux-initrd @ 0x5f13000, 0xcc199 bytes]

grub> boot

Uncompressing Linux... Ok, booting the kernel.

Если известно название ядра, которое вы хотите загрузить, то просто введите символ прямого слэша (/) и затем нажмите клавишу Tab. После этого GRUB отобразит список ядер и образов

initrd

.

После того как образ ядра оказывается в памяти и ему передается управление от загрузчика 2-й ступени, наступает стадия ядра. Однако образ ядра не является исполняемым, это сжатый образ ядра. Обычно это zImage (сжатый образ размером менее 512KB) или bzImage (большой сжатый образ, размером более 512KB), который был сжат при помощи zlib. В начале такого образа ядра располагается программа, которая выполняет минимальную настройку аппаратного обеспечения и затем распаковывает ядро, хранящееся внутри образа ядра, и помещает его в верхнюю область памяти. Если имеется образ начального RAM-диска, то программа также перемещает его в память и помечает для дальнейшего использования, а затем вызывает само ядро, после чего начинается загрузка ядра.

При вызове bzImage (образ для i386) выполнение начинается в

./arch/i386/boot/head.S

с ассемблерной функции

start

(основные этапы показаны на рисунке 3). Эта программа выполняет основную настройку аппаратного обеспечения и вызывает процедуру

startup_32

, располагающуюся в

./arch/i386/boot/compressed/head.S

. Процедура настраивает базовую среду (стек и т.п.) и очищает Block Started by Symbol (BSS). Затем выполняется декомпрессия ядра при помощи вызова C-функции

decompress_kernel

(которая хранится в

./arch/i386/boot/compressed/misc.c

). После декомпрессии ядра в память происходит его вызов. Это еще одна функция

startup_32

, но она располагается в файле

./arch/i386/kernel/head.S.

В новой функции

startup_32

(которая называется swapper или process 0) инициализируются таблицы страниц (page tables) и обеспечивается подключение функции memory paging (отображение страниц). Также определяется тип центрального процессора и сопроцессора для вычислений с плавающей точкой (FPU), если он имеется, и данная информация сохраняется для последующего использования. Далее вызывается функция

start_kernel

из(

init/main.c

), которая осуществляет переход в ту часть ядра Linux, которая не зависит от особенностей конкретной аппаратной платформы. Можно сказать, что это функция

main

для ядра Linux.
Рисунок 3. Выполнение основных функций при загрузке ядра Linux для i386
Основные функции в процессе загрузки ядра Linux i386

При обращении к

start_kernel

вызывается длинный список функций инициализации, которые выполняют настройку прерываний, производят дальнейшее конфигурирование памяти и загружают начальный RAM-диск. После этого вызывается функция

kernel_thread

(из

arch/i386/kernel/process.c

), запускающая функцию

init

, которая является первым процессом, выполняющимся в пространстве пользователя. В заключение запускается idle task, после чего управление может взять на себя планировщик (scheduler) (после вызова

cpu_idle

). Если разрешены прерывания, вытесняющий планировщик (pre-emptive scheduler) будет периодически перехватывать контроль для поддержки многозадачности.

В процессе загрузки ядра выполняется загрузка в оперативную память и монтирование начального RAM-диска (

initrd

), который был загружен в память загрузчиком 2-й ступени. Данный

initrd

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

initrd

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

pivot_root

); при этом корневая файловая система

initrd

удаляется и монтируется действительная корневая файловая система.

Вывод функции decompress_kernel

Функция

decompress_kernel

отвечает за те обычные сообщения о разархивировании, которые появляются на экране :

Uncompressing Linux... Ok, booting the kernel.

Функция

initrd

позволяет создать компактное ядро Linux, где драйверы скомпилированы как загружаемые модули. Эти загружаемые модули обеспечивают доступ ядра к дискам и файловым системам, которые имеются на этих дисках. Также имеются драйверы для других аппаратных устройств. Так как корневая файловая система представляет собой файловую систему на диске, то функция

initrd

обеспечивает для загрузчика возможность обратиться к диску и смонтировать действительную корневую файловую систему. Во встраиваемой системе без жесткого диска

initrd

может представлять собой окончательную файловую систему, или же окончательная файловая система может монтироваться при помощи сетевой файловой системы (Network File System, NFS).

В начало

Init

После загрузки и инициализации ядра запускается первое приложение в пространстве пользователя. Это первая из вызываемых программ, которые скомпилированы со стандартной библиотекой C. До этого момента процесса стандартные C-приложения еще не выполнялись.

На обычных настольных системах с операционной системой Linux обычно первым запускаемым приложением является

/sbin/init

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

init

(которая конфигурируется при помощи

/etc/inittab

). Во многих случаях можно запустить простой shell-скрипт, который запускает необходимые встраиваемые приложения.

В начало

Заключение

Как и сама операционная система Linux, процесс загрузки ядра является чрезвычайно гибким и универсальным и поддерживает большое количество процессоров и аппаратных платформ. В самом начале загрузчик loadlin обеспечивал простой способ загрузки Linux без поддержки каких-либо необязательных аксессуаров. Загрузчик LILO расширил круг поддерживаемых функций, однако его недостатком являлось отсутствие поддержки файловых систем. Последнее поколение загрузчиков, таких как GRUB, позволяет загружать Linux с самых различных файловых систем (начиная с Minix и заканчивая Reiser).

 

Ресурсы

Научиться

  • Оригинал статьи: Inside the Linux boot process (EN).
  • Boot Records Revealed — превосходный ресурс, посвященный MBR и различным загрузчикам. На нем не только рассматривается структура MBR, но и обсуждаются GRUB, LILO и различные Windows®-загрузчики.(EN)
  • Обратитесь к странице DiskGeometry, если вы хотите понять диски и их геометрию. Здесь вы найдете интересную обзорную информацию по свойствам дисков.(EN)
  • live CD — это операционная система, которая может загружаться с CD или DVD, без использования жесткого диска.(EN)
  • Статья «Boot loader showdown: Getting to know LILO and GRUB» (EN) (developerWorks, август 2005 г.) дает детальный обзор возможностей загрузчиков LILO и GRUB.
  • В материалах для подготовки к экзамену Linux Professional Institute (LPI) — серии учебных материалов в разделе учебных пособий developerWorks — содержится детальное введение в загрузку операционной системы Linux, а также описывается много других фундаментальных задач Linux, которые могут оказаться полезными при подготовке к сертификации на системного администратора.
  • Загрузчик LILO был предшественником GRUB, однако и сейчас используется для загрузки Linux.(EN)
  • Команда mkintrd используется для создания образа начального RAM-диска. Эта команда полезна для создания начальной корневой системы для конфигурации загрузчика, обеспечивающей предварительную загрузку блочных устройств, необходимых для доступа к действительной корневой файловой системе.(EN)
  • На сайте Debian Linux Kernel Project вы можете найти дополнительную информацию по ядру Linux, загрузке и разработке для встраиваемых систем.(EN)
  • В разделе Linux сайта developerWorks вы найдете множество ресурсов для разработчиков Linux.
  • Оставайтесь в курсе новостей, посещая раздел техническтх новостей и Web-трансляций developerWorks.(EN)

Получить продукты и технологии

  • Приложение MicroMonitor предоставляет среду для загрузки для большого количества компактных устройств. Вы можете использовать его для наблюдения за процессом загрузки Linux во встраиваемых приложениях. Приложение портировано на процессоры ARM, XScale, MIPS, PowerPC, Coldfire и Super-H (Hitachi).(EN)
  • GNU GRUB предоставляет оболочку для загрузки, которая поддерживает большое количество опций и отличается высокой гибкостью.(EN)
  • LinuxBIOS — это замена BIOS. Это приложение не только позволяет загрузить Linux, LinuxBIOS само является сжатым ядром Linux.(EN)
  • OpenBIOS — это другой проект переносимой BIOS, который способен работать на самых различных архитектурах, таких как x86, Alpha и AMD64.(EN)
  • На сайте kernel.org вы можете найти самое последнее дерево ядра.(EN)
  • Закажите SEK для Linux — комплекст из двух DVD с новейшими ознакомительными версиями программного обеспечения IBM для Linux: DB2®, Lotus®, Rational®, Tivoli® и WebSphere®.(EN)
  • Используйте ознакомительные версии программного обеспечения IBM, которые можно загрузить непосредственно с сайта developerWorks, в своем следующем проекте для Linux.(EN)

Обсудить

Об авторе

M. Тим Джонс (M. Tim Jones) является архитектором встраиваимого программного обеспечения и автором работ: Программирование Приложений под GNU/Linux, Программирование AI-приложений и Использование BSD-сокетов в различных языках программирования. Он имеет опыт разработки процессоров для геостационарных космических летательных аппаратов, а также разработки архитектуры встраиваемых систем и сетевых протоколов. Сейчас Тим работает инженером-консультантом в корпорации Эмулекс (Emulex Corp.) в г.Лонгмонт, Колорадо.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Закончите арифметическое действие * Лимит времени истёк. Пожалуйста, перезагрузите CAPTCHA.