Каждый раз, когда пользователь нажимает кнопку питания своего компьютера, до того, как система полностью будет загружена, происходят несколько ключевых этапов:
Сначала запускается BIOS - Basic Input/Output System (Базовая система ввода/вывода). BIOS не является полноценной операционной системой: это прошивка материнской платы, которая выполняет начальную инициализацию оборудования и передает управление загрузчику. В начале запуска компьютера BIOS проводит процедуру POST (Power-On Self-Test) - проверяет работоспособность оборудования (процессора, ОЗУ и так далее)
BIOS узнает о загрузочном коде напрямую из таблицы MBR (Master Boot Record, Главная загрузочная запись), которая расположена в начале любого диска, с которого можно загрузится (или через PXE - Preboot eXecution Environment)
Master Boot Record занимает первый 512 байт (первый сектор на диске) и содержит:
0x55AAСейчас же вместо BIOS повсеместно используется UEFI - Unified Extensible Firmware Interface (Единый расширяемый интерфейс прошивки). UEFI - это более современная прошивка, которая поддерживает графический интерфейс, управление мышью и обычно работает с таблицей разделов GPT (GUID Partition Table)
Программный код BIOS и UEFI расположены непосредственно на микросхеме на материнской плате компьютера
Далее рассмотрим загрузку Linux в BIOS. BIOS загружает выбранный исполняемый код, который находится в MBR, в ОЗУ, и процессор исполняет его. Для загрузки Linux используют особой компонент - загрузчик. Сейчас самый используемый - это GNU GRUB (GRand Unified Bootloader) версии 2
Код из Master Boot Record содержит 1 фазу загрузчика GRUB boot.img - он занимает 440 байт, что недостаточно для полноценной загрузки
boot.img далее запускает фазу 1.5 загрузчика - core.img. Он находится между MBR и первым разделом диска, в нем расположен код базового набора драйверов для файловых систем (ext4, xfs и так далее), для работы с массивом RAID или LVM (Logical Volume Manager, менеджер логических томов). Как правило, размер core.img составляет от 30 до 40 Кб (как правило 62 сектора на диске, максимальный размер - 458 240 байт)
После этого core.img уже может читать файлы из раздела загрузчика (после загрузки он монтируется как /boot/grub и как правило, находится на другом разделе, чем /). core.img загружает код загрузчика из /boot/grub, который прочитывает файл grub.cfg, где находится выбор ОС (в случае Linux это выбор ядра или его версии) - так начинается фаза 2. GRUB запускает графическое меню, где можно выбрать ядро и его параметры:

Для компьютеров с UEFI все намного проще. UEFI является намного более умным, чем BIOS, и его память на материнской плате содержит драйвера для работы с дисками (в частности для файловых систем FAT16 и FAT32), устройствами USB и сетью
Также UEFI хранит загрузочные записи - они хранят идентификатор GUID раздела и путь до кода загрузчика в специальном разделе ESP (EFI System Partition)
Далее при запуске UEFI ищет на диске этот скрытый раздел ESP, который отформатирован в FAT16 или FAT32. На этом разделе хранятся загрузчики разных ОС и другие инструменты (например, для проверки памяти или для отладки ядра)
При загрузке Linux этот раздел монтируется в /boot/EFI. Структура этого каталога выглядит примерно так:
/boot/EFI
├── ARCH
│ └── grubx64.efi
├── BOOT
│ ├── BOOTIA32.EFI
│ ├── BOOTX64.EFI
│ ├── fbia32.efi
│ └── fbx64.efi
├── Microsoft
│ ├── Boot
│ │ ├── bootmgfw.efi
│ │ ├── bootmgr.efi
│ │ ├── boot.stl
│ │ ├── memtest.efi
│ │ ├── SecureBootRecovery.efi
│ │ └── ...
│ └── Recovery
│ └── ...
└── systemd
└── systemd-bootx64.efi
Главный загрузчик для Windows, Windows Boot Manager, находится в файле Microsoft/bootmgfw.efi. Для Linux (в данном случае дистрибутива Arch Linux) загрузчик расположен в ARCH/grubx64.efi. В BOOT лежит загрузчик по умолчанию: операционная система сначала создает свой загрузчик в BOOT, затем копирует его в отдельную директорию и создает запись в памяти UEFI
Сам файл grubx64.efi представляет собой код из core.img - набор базовый модулей, необходимый для чтения раздела в ext4, конфигурационного файла grub.cfg и загрузки дополнительных модулей
Помимо этого UEFI еще может загружаться через сеть. Для этого он, предварительно получив IP-адрес по протоколу DHCP, скачивает по протоколу TFTP файл bootx64.efi и запускает его
После выбора записи загрузчик GRUB помещает в память образ ядра Linux vmlinuz и образ начальной файловой системы initramfs, а затем передает управление ядру
Для управления загрузкой используется строка аргументов (та, что в последствии находится в /proc/cmdline). В командной строке ядра можно указать путь до vmlinux, путь до корневого раздела (в том числе можно указать UUID диска), режимы загрузки (например, однопользовательский), настройки оборудования, ограничения ресурсов (таких как ОЗУ или ядра ЦПУ) и подобное. Например, строка:
linux /vmlinuz-5.15.0 root=/dev/sda1 ro quiet splash
запускает ядро из /vmlinuz-5.15.0, корневой раздел с диска /dev/sda1, который доступен только в режиме для чтения, с подавлением большинства сообщений ядра (“тихий режим”) и с графическим экраном загрузки
Сам файл vmlinuz - это сжатый образ ядра. Исторически название расшифровывают как vm (virtual memory, виртуальная память) + linu (linux) + z (zipped - сжатый образ). В оперативной памяти ядро распаковывается, переходит в защищенный режим работы процессора, настраивает таблицы страниц, обработчики прерываний, планировщик и начинает обнаружение устройств
На этом этапе ядро еще не может сразу смонтировать настоящую корневую файловую систему /, потому что для этого часто нужны дополнительные модули и драйверы
Именно поэтому вместе с ядром загружается initramfs (от Initial RAM Filesystem) - небольшой архив, который распаковывается в оперативную память и превращается во временную корневую файловую систему
Внутри initramfs обычно находятся:
Раньше до версии ядра 2.6 вместо initramfs использовался initrd (Initial RAM Disk), который монтировался как блочное устройства
Обычно дальше идут такие процессы:
initramfs в память/init. /init загружает нужные модули ядра и подготавливает устройстваinitramfs, который находится в ОЗУ, в файловую систему раздела на дискеЕсли корневой раздел недоступен, система часто попадает в аварийную оболочку из initramfs, где администратор может вручную диагностировать проблему
Когда настоящее корневое дерево каталогов уже доступно, ядро запускает первый пользовательский процесс с идентификатором PID = 1. Исторически это была программа init, а в современных дистрибутивах чаще всего таким процессом становится systemd
Процесс PID = 1 - это особый процесс, так как:
Если ядро не может запустить процесс с PID = 1, загрузка прекращается с паникой ядра
Классическая система инициализации init, которая появилась в UNIX System III и получила развитие в UNIX System V (сейчас такой init называют SysV init), использовала набор sh-скриптов из каталогов вида /etc/rc*.d/. Они запускались последовательно в соответствии с выбранным уровнем выполнения (так называемым runlevel), который описаны в /etc/inittab.Формат строк в /etc/inittab такой:
<идентификатор>:<уровни исполнения>:<действие>:<командная строка запуска>
В System V уровни выполнения в init были такими:
0 - остановка системы1 - однопользовательский режим2 - многопользовательский режим без доступа в Интернет3 - многопользовательский режим4 - не используется или переопределяется администратором5 - многопользовательский графический режим6 - перезагрузкаВ распространенных дистрибутивах, таких как Debian, уровни означали другое:
0 - остановка системы1 - режим восстановления2, 3, 4 - частичный режим, где не все службы запущены5 - полноценный режим6 - перезагрузкаУровень 1 обычно предназначен для загрузки ОС и ее восстановления
В файле /etc/inittab поле <действие> определяет режим запуска процесса, например:
wait - запустить и ждать завершения, до тех пор не продолжать запуск других процессовrespawn - перезапускать после завершенияsysinit - выполнить на раннем этапе загрузкиonce - запустить и продолжитьctrlaltdel - запустить, если нажата комбинация Ctrl+Alt+Deloff - если процесс выполняется, то он будет принудительно завершенpowerfall - процесс запускается, если init получил сигнал SIGPWR (проблема с питанием)boot - процесс запускается при загрузке системы до перехода на любой уровень выполненияbootwait - процесс запускается при загрузке системы, а init дожидается его завершенияСам файл /etc/inittab может выглядеть так:
# Уровень выполнения по умолчанию
id:5:initdefault:
# Скрипт, выполняемый при загрузке системы (до уровней выполнения)
si::sysinit:/etc/rc.d/rc.sysinit
# Запуск скриптов для конкретного уровня выполнения
l0:0:wait:/etc/init.d/rc 0
l1:1:wait:/etc/init.d/rc 1
l2:2:wait:/etc/init.d/rc 2
l3:3:wait:/etc/init.d/rc 3
l4:4:wait:/etc/init.d/rc 4
l5:5:wait:/etc/init.d/rc 5
l6:6:wait:/etc/init.d/rc 6
# Действие при нажатии Ctrl+Alt+Del - перезагрузка
ca::ctrlaltdel:/sbin/shutdown -t3 -r now
# Действие при кратковременном сбое питания (перезагрузка через 2 минуты)
pf::powerwait:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"
# Запуск виртуальной консоли
1:2345:respawn:/sbin/mingetty tty1
Здесь /etc/init.d/rc - скрипт, который запускает другие скрипты из каталога /etc/rc<уровень>.d. Уровень - это первый аргумент вызова /etc/init.d/rc
В каталоге /etc/rc.d/rc<уровень>.d скрипты обычно имеют такое наименование: K<NN><имя_сервиса> или S<NN><имя_сервиса>. Буква K означает, что сервис нужно остановить при выходе из уровня, а S означает, что его нужно запустить при переходе в этот уровень
Другая команда, sysinit, выполняется один раз перед переходом в любой уровень исполнения. Ее задачи:
/proc, /sys, /dev/etc/modules)В большинстве современных дистрибутивов сейчас вместо SysV init используется systemd. Он запускает службы параллельно, использует декларативные файлы вида и умеет отслеживать зависимости между компонентами системы
О нем подробно описано в разделе systemd
Подытоживая, загрузку ОС Linux можно описать так:
vmlinuz и initramfsinitramfs помогает ядру найти и смонтировать настоящий корневой разделPID = 1 (init или systemd) запускает службы и переводит систему в рабочее состояние
Последовательность загрузок разных частей операционной системы расширяет функционал до тех пор, пока система может взаимодействовать с оборудованием, и решает проблему “курицы и яйца”