Linux под капотом: как система загружается на самом деле (UEFI, GRUB, ядро, initramfs, systemd)

Загрузка Linux обычно выглядит скучно: нажал кнопку, побежали строчки, появился логин. Но внутри за пару секунд происходит цепочка шагов, где любой мелкий косяк превращается в «чёрный экран», «зависло на A start job is running…» или «почему сервер грузится 3 минуты».

В этой части рубрики «Linux под капотом» разберу загрузку от момента включения питания до приглашения входа. Без мифов и без «оно само». С командами, где это можно руками потрогать и измерить.

Карта загрузки в двух словах

Если свести весь процесс к «кто за кем»:

  • прошивка (BIOS/UEFI) выбирает, откуда грузиться;
  • загрузчик (чаще GRUB, иногда systemd-boot) загружает ядро и initramfs;
  • ядро Linux поднимает драйверы, память, планировщик и стартует раннее пользовательское окружение из initramfs;
  • initramfs находит корневую ФС и передаёт управление системе (init);
  • init (обычно systemd) запускает сервисы, монтирует всё нужное, поднимает сеть, логин и т.д.

Дальше по шагам: что именно происходит и где это смотреть.

Шаг 1: BIOS/UEFI выбирает, откуда грузиться

На железе первым просыпается прошивка. В старом мире это BIOS, в новом уже UEFI. Для Linux-админа важны две вещи: режим загрузки (UEFI или Legacy) и то, где лежит загрузчик.

Linux под капотом: как система загружается на самом деле (UEFI, GRUB, ядро, initramfs, systemd)

Как понять, в каком режиме загрузилась система:

[ -d /sys/firmware/efi ] && echo "UEFI" || echo "Legacy BIOS"

Если UEFI, значит где-то есть EFI-раздел (обычно FAT32), и в нём лежат .efi-файлы загрузчика. Если Legacy, история больше похожа на «MBR + GRUB stage».

Посмотреть EFI-раздел и как он смонтирован:

lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT,PARTUUID
findmnt /boot/efi || true

Если на сервере внезапно поменялся режим (перенос диска, виртуалка, пересоздание VM), дальше начинается цирк: GRUB стоит «не туда», запись в NVRAM не совпадает, systemd-boot не видит ядро. Поэтому шаг «UEFI/Legacy» это не формальность, а диагностика в первые 30 секунд.

Шаг 2: загрузчик (GRUB или systemd-boot)

Загрузчик делает простую, но критичную вещь: выбирает ядро, передаёт параметры командной строки, подцепляет initramfs и запускает ядро. В большинстве дистрибутивов это GRUB. В некоторых системах (особенно чистых UEFI-сборках) может быть systemd-boot.

Если GRUB, то чаще всего конфиг живёт здесь:

ls -l /boot/grub/ /boot/grub2/ 2>/dev/null || true
ls -l /etc/default/grub 2>/dev/null || true

Если systemd-boot:

bootctl status 2>/dev/null || true
ls -l /boot/loader/entries 2>/dev/null || true

Самое полезное в повседневной жизни: параметры ядра. Они решают «какой root», «как логировать», «в какой таргет грузиться», «вырубить ли splash и увидеть ошибки».

Посмотреть, с чем ядро реально загрузилось:

cat /proc/cmdline

Два параметра, которые часто выручают при отладке загрузки:

  • systemd.unit=rescue.target: загрузка в rescue (минимум сервисов);
  • systemd.unit=emergency.target: ещё жёстче, по сути «влететь в shell и не трогать лишнее».

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

Шаг 3: ядро Linux стартует и пишет первые логи

Ядро загружается в память, распаковывается, инициализирует железо, драйверы, планировщик, память, файловые системы. Всё это видно в kernel ring buffer, то самое, что показывает dmesg.

Посмотреть сообщения ядра текущей загрузки:

dmesg -T | less

А если нужно поймать именно ошибки и предупреждения:

dmesg -T --level=err,warn | less

На этом этапе типовые проблемы выглядят так: «не нашёл диск», «не поднялся драйвер», «ждал устройство», «не смог смонтировать root». Если root не смонтировался – загрузка дальше не пойдёт. И тут в игру вступает initramfs.

Шаг 4: initramfs – «временная система», которая ищет root

initramfs (или initrd) это минимальная файловая система в памяти. Внутри скрипты и драйверы, которые нужны, чтобы:

  • подцепить RAID/LVM/шифрование (LUKS);
  • найти нужный диск;
  • смонтировать корень и передать управление основному init.

В Debian/Ubuntu initramfs обычно собирается через update-initramfs. Быстро посмотреть, какие образы есть:

ls -lh /boot/initrd* /boot/initramfs* 2>/dev/null || true
uname -r

Пересобрать initramfs для текущего ядра (когда, например, добавили драйвер, поменяли LVM, правили hooks):

sudo update-initramfs -u -k "$(uname -r)"

Самый частый сценарий «всё работало, а потом не грузится» – поменяли диски/UUID, правили /etc/fstab, пересобрали RAID или переехали в другую виртуалку, а initramfs остался со старой картиной мира. В итоге ядро стартует, а root найти не может.

Шаг 5: systemd как PID 1 и почему именно он решает, «быстро» или «долго»

Когда root смонтирован, ядро запускает первый пользовательский процесс: init. На большинстве современных систем это systemd. Он становится PID 1 и дальше дирижирует всем: монтирование, сервисы, сеть, логирование, таймеры, зависимости.

Проверить, кто у вас PID 1:

ps -p 1 -o pid,comm,args

Посмотреть, во что система загрузилась (target):

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

Вот тут и рождаются «долгие загрузки». Не потому что «Linux медленный», а потому что какой-то unit ждёт сеть, диск, DNS, таймаут монтирования или внешний ресурс. systemd честно ждёт, пока истечёт таймаут, и вы получаете лишние 90 секунд на ровном месте.

Как посмотреть, что тормозит загрузку

systemd даёт три команды, которые стоит держать под рукой.

Общее время загрузки по этапам (прошивка, загрузчик, ядро, userspace):

systemd-analyze

Самые долгие юниты (не всегда «виновники», но отличная зацепка):

systemd-analyze blame | head -n 30

Цепочка зависимостей до конкретного проблемного юнита. Очень помогает, когда «виновник» не тот, кто висит, а тот, кого он ждёт:

systemd-analyze critical-chain

Если видите что-то вроде dev-disk-by... или network-online.target с большим временем, это классика. Диск не появился вовремя, DNS не поднялся, сетевой «online» ждёт бесконечно, а сервер просто стоит и ждёт, как дисциплинированный.

Где смотреть логи загрузки: journalctl и dmesg

Для диагностики почти всегда стоит начинать с лога текущего бут-цикла.

Логи текущей загрузки:

journalctl -b -0

Логи предыдущей загрузки (когда «упало и перезагрузилось», а вы пришли уже после):

journalctl -b -1

Только ошибки текущей загрузки:

journalctl -b -0 -p err

Если журнал не сохраняется между перезагрузками, диагностика превращается в гадание. Включается это просто: нужен каталог /var/log/journal и рестарт journald.

sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journald

После этого journalctl -b -1 станет реально полезным, а не «пусто, потому что так решили по умолчанию».

Мини-лабораторка: измерь загрузку и найди узкое место

Этот блок можно прогнать на любой Ubuntu/Debian (и вообще на большинстве systemd-систем). Ничего опасного, только наблюдение.

1) Зафиксируйте времена загрузки и топ медленных юнитов:

systemd-analyze
systemd-analyze blame | head -n 25
systemd-analyze critical-chain

2) Найдите один долгий юнит и посмотрите подробности (что он делает и почему ждёт):

systemctl status ИМЯ_ЮНИТА
journalctl -b -0 -u ИМЯ_ЮНИТА --no-pager

3) Проверьте, не упёрся ли сервер в ожидание диска или монтажа. Если в выводах часто мелькают монтирования:

systemctl --failed
findmnt -a | head
cat /etc/fstab

Если в /etc/fstab есть сетевые шары или «второстепенные» диски, которые не всегда доступны, их можно делать более мягкими (например, через nofail и разумные timeout-опции). Но это уже тема для отдельной заметки: «как не убивать загрузку из-за одного капризного диска».

Что дальше в рубрике

Следующий логичный шаг: разбор systemd глубже: unit-файлы, зависимости, targets, таймеры, почему network-online.target иногда зло, и как делать сервисы так, чтобы они стартовали предсказуемо.

А ещё отдельно стоит разобрать initramfs на реальных кейсах: LUKS, LVM, RAID, перенос дисков, восстановление после кривого fstab. Там много боли, но зато после этого «не грузится сервер» перестаёт звучать как приговор.


Комментарии загружаются…