Перейти до вмісту

Від кнопки живлення до shell

Між натисканням кнопки живлення і запрошенням входу минає кілька секунд, за які керування послідовно змінює чотирьох власників. Поки все працює, знати цей ланцюг вам не знадобиться.

Знадобиться, коли щось зламається. Система не завантажується після оновлення ядра. Замість робочого столу з’явився emergency mode. Служба руками запускається, а при завантаженні ні. Кожна така ситуація відповідає конкретній ланці ланцюга, тож половина діагностики — це зрозуміти, до якої ланки система взагалі дійшла.

Передумови. Режими виконання (модуль 2), PID 1 і дерево процесів (модуль 6).

Ланцюг завантаження від прошивки UEFI до systemd із зазначенням, хто саме виконує кожен крок прошивказавантажувачядропростір користувача1прошивка UEFI: самоперевірка, ініціалізація памʼяті й шин2пошук завантажувача на розділі ESP3завантажувач: вибір ядра, параметри командного рядка4ядро розпаковується і бере керування апаратурою на себе5initramfs: драйвери, потрібні щоб дістатися кореня6перемикання на справжній кореневий розділ7запуск PID 1 — першого процесу режиму користувача8systemd розгортає цілі та служби, зʼявляється запрошення входу
Кожна ланка вміє рівно стільки, щоб знайти й запустити наступну. Проблема на будь-якому кроці зупиняє все, що після нього — тому діагностика починається з питання, до якого кроку система дійшла.

Починається все з прошивки. Процесор бере першу інструкцію з фіксованої адреси, де вона й лежить. Прошивка перевіряє та ініціалізує пам’ять, шини й контролери, після чого шукає, що завантажувати далі.

Сучасна прошивка — це UEFI. Вона розуміє файлову систему FAT32 і читає завантажувач як звичайний файл з окремого розділу, який називається ESP (EFI System Partition). Список записів лежить в енергонезалежній пам’яті материнської плати, і його можна редагувати з працюючої системи через efibootmgr.

Стара схема BIOS + MBR влаштована зовсім інакше: BIOS зчитує перші 512 байтів диска й виконує їх. У ці 512 байтів разом із таблицею розділів вміщається так мало коду, що завантажувач доводилося ділити на етапи. Звідти ж походять і обмеження MBR — чотири первинні розділи та 2 ТіБ, які GPT знімає обидва.

Завантажувач, GRUB чи systemd-boot, показує меню, знаходить образ ядра й initramfs і передає ядру параметри командного рядка. Цей рядок визначає, що ядро робитиме далі: де шукати корінь, чи вмикати налагоджувальний вивід, які драйвери вимкнути.

Ядро розпаковується, ініціалізує підсистеми, знаходить процесори й пам’ять, запускає драйвери і монтує initramfs.

Останнім ядро запускає перший процес режиму користувача, PID 1. Він особливий одразу з кількох боків: його не вбити звичайним сигналом, він усиновлює всіх процесів-сиріт (модуль 6), а якщо він завершиться, ядро зупинить систему.

Класичний init запускав скрипти послідовно за номерами. Просто й повільно: кожна служба чекала на попередню, навіть коли між ними не було жодного зв’язку.

systemd натомість описує систему як граф залежностей і паралельно запускає все, що можна запустити паралельно.

Одиниця, якою він керує, називається юнітом. Типів кілька: .service для процесу, .socket для сокета, при зверненні до якого служба запускається, .timer замість cron, .mount для точки монтування і .target для групи юнітів.

Ціль варто розуміти не як рівень виконання, а як точку в графі. multi-user.target означає, що система готова до роботи в консолі, а graphical.target — що з графічним середовищем.

Найцікавіше тут — активація за сокетом. Systemd відкриває сокет одразу, а службу запускає лише при першому зверненні. Клієнт різниці не бачить, бо з’єднання приймається і чекає, зате завантаження не гальмує через служби, до яких узагалі ніхто не звертається.

Terminal window
[ -d /sys/firmware/efi ] && echo 'UEFI' || echo 'BIOS (legacy)'

Чи завантажена система через UEFI. Каталог існує лише в режимі UEFI.

Terminal window
efibootmgr -v 2>/dev/null | head; lsblk -o NAME,PARTTYPENAME,MOUNTPOINT | head

Записи завантаження в пам’яті материнської плати і розділ ESP, зазвичай змонтований у /boot/efi.

Terminal window
cat /proc/cmdline

Параметри, з якими завантажене поточне ядро — саме те, що передав завантажувач. Тут видно, звідки взято корінь і які опції застосовані.

Terminal window
lsinitramfs /boot/initrd.img 2>/dev/null | head -20 || lsinitrd 2>/dev/null | head -20

Вміст initramfs: набір модулів, потрібних, щоб дістатися кореня.

Тут навмисно /boot/initrd.img — символьне посилання на образ типового ядра. Якщо взяти /boot/initrd.img-$(uname -r), команда впаде саме тоді, коли ситуація найцікавіша: після оновлення ядра, коли новий образ уже є, а система досі виконує старе ядро, для якого образу може не бути. Порівняти обидва — корисна вправа.

Terminal window
systemd-analyze; systemd-analyze blame | head -10

Скільки тривало завантаження з розбивкою на прошивку, завантажувач, ядро й простір користувача — і які юніти забрали найбільше часу.

Terminal window
systemd-analyze critical-chain

Критичний ланцюг: послідовність, що фактично визначила час завантаження. Оптимізувати має сенс лише те, що в ньому.

Terminal window
systemctl list-units --type=target --state=active
systemctl get-default

Активні цілі й ціль за замовчуванням.

Terminal window
journalctl -b -p err --no-pager | tail -10

Помилки поточного завантаження. -b -1 покаже попереднє — незамінне, коли система впала і встигла перезавантажитись.

Типові помилки розуміння

Section titled “Типові помилки розуміння”

«BIOS і UEFI — те саме, просто нова назва». Це різні схеми. BIOS виконує перші 512 байтів диска, а UEFI читає .efi-файл із розділу FAT32 і зберігає список записів у пам’яті материнської плати.

«initramfs потрібен для швидкості». Він потрібен, щоб розірвати коло: для монтування кореня потрібні драйвери, які лежать на самому корені.

«systemd — це просто init». Це менеджер служб із графом залежностей, активацією за сокетом, таймерами, точками монтування й журналом. Спірним його вважають саме через обсяг того, що він на себе забрав.

«Ціль — це рівень виконання». Цілі не впорядковані числом і не виключають одна одну, це просто точки синхронізації в графі.

«Служба працює руками, значить юніт правильний». Оточення в юніта інше: немає ваших змінних, інший робочий каталог, stdout у journald. Розбіжність найчастіше саме тут.

«Система не завантажилась, перевстановлю». Спершу подивіться journalctl -b -1 -p err і параметри в /proc/cmdline. Найчастіше винні дві речі: не перезібраний після оновлення ядра initramfs і неправильний UUID кореня у /etc/fstab.

Перевір себе

1. Навіщо потрібен initramfs?
2. Чим UEFI принципово відрізняється від BIOS?
3. Що станеться, якщо процес із PID 1 завершиться?
4. Служба запускається руками, але падає при старті через systemd. З чого почати?
5. Що дає активація за сокетом у systemd?
6. Де подивитися, з якими параметрами завантажене поточне ядро?

B1 — встановлення системи. Розмітка GPT, розділ ESP, завантаження через UEFI і власний .service-юніт із залежностями. Обов’язкова частина — навмисно зламати завантаження й полагодити його з аварійного режиму.

  • Nemeth та ін., UNIX and Linux System Administration Handbook, розділ 2
  • man 7 boot, man 1 systemd, man 5 systemd.unit, man 5 systemd.service
  • systemd for Administrators — серія від автора
  • UEFI Specification — якщо потрібні деталі формату