itmo_conspects

Лекция 6. Загрузка ОС Linux

Каждый раз, когда пользователь нажимает кнопку питания своего компьютера, до того, как система полностью будет загружена, происходят несколько ключевых этапов:

Загрузка BIOS/UEFI

Сначала запускается 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 байт (первый сектор на диске) и содержит:

Сейчас же вместо BIOS повсеместно используется UEFI - Unified Extensible Firmware Interface (Единый расширяемый интерфейс прошивки). UEFI - это более современная прошивка, которая поддерживает графический интерфейс, управление мышью и обычно работает с таблицей разделов GPT (GUID Partition Table)

Программный код BIOS и UEFI расположены непосредственно на микросхеме на материнской плате компьютера

Загрузчик GRUB

Далее рассмотрим загрузку Linux в BIOS. BIOS загружает выбранный исполняемый код, который находится в MBR, в ОЗУ, и процессор исполняет его. Для загрузки Linux используют особой компонент - загрузчик. Сейчас самый используемый - это GNU GRUB (GRand Unified Bootloader) версии 2

BIOS

Код из 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 запускает графическое меню, где можно выбрать ядро и его параметры:

Меню GRUB

UEFI

Для компьютеров с 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, а затем передает управление ядру

Сам файл vmlinuz - это сжатый образ ядра. Исторически название расшифровывают как vm (virtual memory, виртуальная память) + linu (linux) + z (zipped - сжатый образ). В оперативной памяти ядро распаковывается, переходит в защищенный режим работы процессора, настраивает таблицы страниц, обработчики прерываний, планировщик и начинает обнаружение устройств

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

Именно поэтому вместе с ядром загружается initramfs (от Initial RAM Filesystem) - небольшой архив, который распаковывается в оперативную память и превращается во временную корневую файловую систему

Внутри initramfs обычно находятся:

Раньше до версии ядра 2.6 вместо initramfs использовался initrd (Initial RAM Disk), который монтировался как блочное устройства

Обычно дальше идут такие процессы:

  1. Ядро распаковывает initramfs в память
  2. Запускается ранняя пользовательская программа /init. /init загружает нужные модули ядра и подготавливает устройства
  3. Находится и монтируется настоящий корневой раздел. Затем выполняется переключение корня из initramfs, который находится в ОЗУ, в файловую систему раздела на диске
  4. Управление передается уже обычной системе в корневом разделе

Если корневой раздел недоступен, система часто попадает в аварийную оболочку из 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 были такими:

В распространенных дистрибутивах, таких как Debian, уровни означали другое:

Уровень 1 обычно предназначен для загрузки ОС и ее восстановления

В файле /etc/inittab поле <действие> определяет режим запуска процесса, например: wait - запустить и ждать завершения, respawn - перезапускать после завершения, sysinit - выполнить на раннем этапе загрузки


В большинстве современных дистрибутивов вместо SysV init используется systemd. Он запускает службы параллельно, использует декларативные файлы вида /etc/systemd/system/myapp.type и умеет отслеживать зависимости между компонентами системы

Службы в systemd имеют тип:

Тип службы определяет показывает, какой объект системы он описывает. Так, /etc/systemd/system/myapp.service - это служба. Внутри текстовый файл содержит ее описание:

[Unit]
Description=My Application
After=network.target        # запустить после запуска демона networkd

[Service]
Type=simple
User=myappuser              # запустить от имени myappuser
ExecStart=/usr/bin/myapp    # исполняемый файл
Restart=on-failure          # перезапустить при падении
StandardOutput=journal      # записывать логи в журнал systemd

При загрузке systemd читает конфигурацию, монтирует файловые системы, поднимает сеть, запускает системные службы и доводит систему до нужного целевого состояния, например до multi-user.target (многопользовательский режим) или graphical.target (режим с графической оболочкой)


Подытоживая, загрузку ОС Linux можно описать так:

Загрузка ОС Linux

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