Опубликован: 22.06.2005 | Уровень: для всех | Доступ: свободно | ВУЗ: Компания IBM
Лекция 10:

Этапы загрузки системы

< Лекция 9 || Лекция 10: 123456 || Лекция 11 >

Загрузка системы

С запуска init начинается загрузка самой системы. Во времена молодости Linux и ранее в этом месте никаких подводных камней не наблюдалось. Если ядро содержало подпрограммы для работы со всеми необходимыми устройствами (так называемые "драйверы"), оно загружалось и запускало init. Если ядру недоставало каких-то важных драйверов (например, поддержки дискового массива, с которого и шла загрузка) - оно не загружалось и не запускало. Из положения выходили просто: в ядро старались включить как можно больше драйверов. Такое ядро называлось базовым (generic) и имело довольно внушительный размер. Загрузив систему с базовым ядром, администратор обычно пересобирал его: выбрасывал из специального файла-профиля драйверы всех отсутствующих в системе устройств, быть может, добавлял новые (те, что не нужны для загрузки, но необходимы для работы, например, звуковые) и компилировал из исходных текстов новое, профильное ядро.

Стартовый виртуальный диск и модули ядра

Пересборка ядра в наше время требуется очень редко. Во-первых, в Linux поддерживается несметное количество различных внешних устройств, драйверы которых (особенно похожих, но разных) вполне могут помешать друг другу работать, если их использовать одновременно. Пришлось бы собирать множество разных ядер, без возможности указать пользователю, какое из них подходит для его компьютера. Во-вторых, выяснением того, какой именно драйвер необходим найденному устройству, занимаются сейчас специальные программы, в распоряжении которых есть целые базы данных - ядру такую работу выполнять неудобно, да и незачем. Это делает процедуру пересборки ядра почти что обязательной (пока не загружено базовое ядро, непонятно, какие драйверы добавлять в профильное ). А в-третьих, пересборка ядра требует весьма высокой квалификации. Этот процесс нельзя ни автоматизировать, ни упростить. Утилита linuxconf, устроенная именно для этого на основе окон и меню, дает на выходе работоспособное ядро в трех случаях: (1) в руках профессионала, (2) при четком следовании полной инструкции и (3) по случайности5Не надо вручную пересобирать ядро, даже если в учебнике по Linux рекомендуется это сделать! .

Совсем другие времена настали, когда изобрели и активно внедрили в Linux загружаемые модули ядра. Модуль ядра - это часть ядра Linux, которую можно добавлять и удалять во время работы системы. Модуль ядра - не процесс, он работает в режиме супервизора и в таблице процессов не регистрируется: это набор подпрограмм для работы с определенным устройством, которые добавляются к возможностям ядра6Этим он скорее похож на динамическую библиотеку. . При загрузке в память модуль компонуется с ядром, образуя с ним одно целое. Просмотреть список загруженных модулей можно командой lsmod, а подгрузить модуль в память, добавив его к ядру, и удалить его оттуда - командами insmod и rmmod соответственно.

# lsmod
Module               Size       Used by      Not tainted
usb-uhci            21676       0            (unused)
usbcore             58464       1            [usb-uhci]
af_packet           12392       1            (autoclean)
pcnet32             15140       1            (autoclean)
mii                  2544       0            (autoclean) [pcnet32]
crc32                2880       0            (autoclean) [pcnet32]
floppy              48568       0            (autoclean)
subfs                4296       4            (autoclean)
ac                   1792       0            
rtc                  6236       0            (autoclean)
ext3                62288       2            
jbd                 37852       2            [ext3]
Пример 10.4. Получение списка загруженных модулей

Изменилось и базовое ядро: теперь оно включает в себя только устройства, необходимые для загрузки системы: главным образом диски и графическую консоль. Остальные устройства определятся уже самой системой - тогда можно будет и распознать экзотическую аппаратуру, и модуль для нее подгрузить. Однако полностью перевести драйверы всех внешних устройств в модули мешает следующее соображение: что, если загрузка системы происходит именно с того устройства, чей модуль еще не загружен в ядро, например, с дискового массива (RAID)? Вторичный загрузчик и ядро можно, недолго думая, разместить на другом носителе (например, на лазерном диске) или добыть с дискового массива средствами BIOS ( карты размещения позволяют не обращать внимания на логическую структуру RAID). Но как добыть модуль работы с RAID, тот самый, что распознает эту логическую структуру?

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

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

Вдумаемся. Для того чтобы средствами Linux подключить модуль ядра, работающий с дисковым устройством, необходимо загрузить Linux с этого же устройства. Так ли это невозможно? Ведь если можно прочесть оттуда " ядро ", то, наверное, можно прочесть и "Linux"? Более точно, вдобавок к одной области данных, соответствующей ядру, надо прочитать вторую, соответствующую некоторой уменьшенной до предела установке Linux, в которой содержатся только нужные программы и модули, загрузить оттуда "маленький Linux", который настроит и подключит злополучный RAID и запустит процесс загрузки полноценной системы оттуда.

Предельно сжатый вариант Linux есть - это проект busybox, используемый во встроенных системах, где дорог каждый байт. Разместить файловую систему в памяти тоже легко - этим, например, занимается модуль tmpfs, который можно включить в базовое ядро (подробнее о типах файловых систем будет рассказано в лекции 11). Осталось только обучить подсистемы загрузки GRUB и LILO считывать не одну, а две области данных - ядро и образ файловой системы. Ядру при этом передается параметр "пользуйся виртуальным диском", чтобы оно подключило загруженный образ в качестве временной корневой файловой системы. Можно также потребовать, чтобы память, занимаемая временной файловой системой, освобождалась в процессе дальнейшей загрузки.

Такой механизм называется initrd ( init ial r am d isk, где "ram" - это не "баран", а r andom a ccess m emory, то есть оперативная память) или стартовым виртуальным диском. Стартовый виртуальный диск собирается по команде mkinitrd в соответствии с профилем компьютера и записывается на диск по тем же правилам, что и ядро. В примере двухсистемной машины, за которой работал Мефодий, также был стартовый виртуальный диск, причем довольно маленький:

[root@localhost root]# ls -lg /boot
drwxr-xr-x 2 root   4096 Nov 20 21:08 grub
-rw------- 1 root 205374 Nov  9 01:33 initrd-2.4.26-std-up.img
lrwxrwxrwx 1 root     29 Nov  9 01:33 initrd-up.img ->
initrd-2.4.26-std-up.img
-rw------- 1 root  45056 Nov 20 19:07 map
-rw-r--r-- 1 root 935892 Aug  3 21:59 vmlinuz-2.4.26-std-up
lrwxrwxrwx 1 root     26 Nov  9 01:33 vmlinuz-up -> vmlinuz-2.4.26-std-up
Пример 10.5. Размеры и наименование файлов с ядром и стартовым виртуальным диском

Как видно из примера, ядро в четыре раза превосходит по размеру стартовый виртуальный диск. Стоит заметить, что и ядро, и образ диска упакованы с помощью утилиты gzip (причем ядро умеет распаковываться в памяти самостоятельно), поэтому их действительный размер - больше. В файле map хранится карта размещения LILO, а упомянутые в lilo.conf и menu.lst файлы vmlinuz-up и initrd-up.img оказались символьными ссылками на файлы с более "говорящими" именами. Никаких требований к названиям ядер в Linux нет, это дело авторов дистрибутива. В этом случае в имени ядра и образа диска встречается версия ядра (2.4.26), тип сборки std (по-видимому, "standard") и тип архитектуры up ( u ni p rocessor, т.е. однопроцессорная).

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

Отец всех процессов

Если в параметрах не указано иное, ядро считает, что init называется /sbin/init. В стартовом виртуальном диске это обычно некоторый простейший сценарий, а в полноценной системе у init другая задача: он запускает все процессы. Если процессы запускает не он сам, то это делают его потомки, так что все процессы Linux, кроме ядерных, происходят от init, как весь род людской - от Адама.

Первым делом init разбирает собственный конфигурационный файл - /etc/inittab. Файл этот имеет довольно простую структуру: каждая строка (если она не комментарий) имеет вид " id:уровни:действие:процесс ", где id - это некоторая двух- или однобуквенная метка, уровни - это слово, каждая буква которого соответствует уровню выполнения (об уровнях выполнения будет рассказано далее), действие - это способ запуска процесса. Например, запись 4:2345:respawn:/sbin/mingetty tty4 означает, что меткой " 4 " помечен запуск /sbin/mingetty tty4 7 Mingetty - упрощенный аналог getty, работающий только на виртуальных консолях. на уровнях выполнения 2, 3, 4 и 5 по алгоритму " respawn " (запустить в фоне, а когда процесс завершится, запустить заново). Помимо " respawn ", существуют методы " once " (запустить в фоне однократно), " wait " (запустить интерактивно, при этом никаких других действий не выполняется, пока процесс не завершится) и множество других, включая даже " ctrlaltdel " - процесс, запускаемый, когда пользователь нажимает на консоли Ctrl+Alt+Del 8Понятно, что Ctrl+Alt+Del - это не reset, а обычное сочетание клавиш. Для удобства пользователя его специально распознает клавиатурный драйвер, а ядро сообщает об этом init -у. .

Наконец-то Мефодий до конца понял, отчего getty ведет себя так непохоже на остальные процессы: не просто запускает из-под себя login, а дожидается окончания его работы, отсутствуя при этом в таблице процессов. На самом деле дожидается не getty, а init, используя метод " respawn ": порождается (в фоне) процесс getty с определенным PID, а init бездействует до тех пор, пока существует процесс с этим PID: getty, login, стартовый командный интерпретатор или программа, запущенная из него с помощью exec() ; когда же процесс, наконец, умирает, порождается новый getty.

< Лекция 9 || Лекция 10: 123456 || Лекция 11 >
Аягоз Имансакипова
Аягоз Имансакипова
Тимур Булатов
Тимур Булатов

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