Нажата кнопка питания. Через несколько секунд на экране появляется приглашение к входу или рабочий стол. В промежутке между этими двумя моментами разворачивается процесс, который большинство пользователей никогда не видят и редко задумываются о нём. Ядро загружается, распаковывает в память маленькую временную файловую систему, выполняет в ней ряд критически важных задач и только потом передаёт управление настоящей системе. Всё это происходит до того, как systemd получит PID 1. И если что-то идёт не так именно здесь, система не загрузится вообще.
Курица и яйцо и зачем вообще нужен initramfs
Ядро Linux при загрузке сталкивается с классической проблемой зависимостей. Чтобы смонтировать корневую файловую систему, нужны драйверы: для контроллера диска, для файловой системы ext4 или btrfs, для RAID-массива или LVM-тома. Драйверы хранятся на диске в виде модулей ядра. Но чтобы прочитать их с диска, нужно уже иметь доступ к диску. Получается петля.
До появления initramfs её разрывали грубой силой: все нужные драйверы компилировали непосредственно в ядро. Это работало, но делало ядро огромным и негибким. Каждая новая конфигурация оборудования требовала пересборки ядра. Когда появились модульные ядра, проблема встала в полный рост.
Решением стал initramfs, initial RAM filesystem. Загрузчик кладёт в память вместе с ядром небольшой архив, внутри которого уже есть всё необходимое для первого шага: минимальный набор инструментов, нужные модули ядра и скрипты, которые умеют разобраться с конкретной конфигурацией хранилища. Ядро разворачивает этот архив в tmpfs прямо в памяти, получает рабочую файловую систему без обращения к диску и использует её как трамплин для монтирования настоящей корневой файловой системы.
Цепочка от UEFI до первых строк ядра
Прежде чем initramfs вступит в игру, система проходит несколько более ранних этапов. UEFI-прошивка проводит POST, инициализирует базовое железо и ищет загрузчик. Чаще всего это GRUB2 или systemd-boot. Загрузчик читает свою конфигурацию и передаёт ядру два объекта: сам образ ядра (vmlinuz) и образ initramfs. В записи GRUB это выглядит примерно так:
linux /boot/vmlinuz-6.12.0 root=/dev/sda2 quiet splash
initrd /boot/initramfs-6.12.0.img
Ключевой момент: загрузчик просто копирует оба файла в RAM и сообщает ядру их адреса. С этого момента загрузчик выходит из игры.
Ядро распаковывает себя в память, инициализирует подсистемы в жёстко заданном порядке: управление памятью, планировщик, прерывания, базовые драйверы. Затем оно смотрит на командную строку и находит параметр initrd. По указанному адресу в RAM лежит cpio-архив, обычно сжатый zstd, lz4 или xz. Ядро распаковывает его в tmpfs и монтирует получившуюся структуру как корневую файловую систему. Это и есть initramfs в рабочем состоянии.
Что внутри архива и как он устроен
Посмотреть содержимое initramfs можно без перезагрузки. На системах с lsinitrd:
lsinitrd /boot/initramfs-$(uname -r).img | head -50
На системах без этого инструмента распаковать вручную:
mkdir /tmp/initramfs_extracted && cd /tmp/initramfs_extracted
zcat /boot/initramfs-$(uname -r).img | cpio -idmv 2>/dev/null | tail -5
Внутри обнаруживается минималистичное дерево: каталоги bin, sbin, lib, lib64, dev, proc, sys, run. В bin и sbin лежит busybox или набор статически скомпилированных утилит. В lib/modules находятся модули ядра, выбранные при создании образа. Там же лежит init или символическая ссылка на него, в современных системах это /usr/lib/systemd/systemd или busybox-скрипт.
Количество модулей ядра зависит от способа сборки. Образ в режиме hostonly, собранный dracut с опцией -H, содержит только то, что нужно конкретной машине. Образ в generic-режиме включает модули для широкого спектра оборудования и весит значительно больше.
Первый процесс в initramfs и что он делает
После монтирования tmpfs с содержимым архива ядро запускает PID 1 внутри initramfs. Это ещё не тот systemd, который управляет системой. Это systemd или init-скрипт, работающий в рамках временной файловой системы с одной задачей: подготовить корневую файловую систему к монтированию и передать туда управление.
Работа этого временного init разворачивается в несколько последовательных шагов. Сначала монтируются псевдофайловые системы: proc, sys, devtmpfs. Без них невозможна работа с устройствами. Запускается udev или mdev для обнаружения железа и создания нод в /dev. Если нужны дополнительные модули ядра, они загружаются через modprobe:
modprobe nvme
modprobe dm-crypt
Если корневая файловая система зашифрована через LUKS, именно здесь происходит запрос пароля или обращение к TPM2. Dracut вызывает systemd-cryptsetup, который монтирует зашифрованный том:
systemd-cryptsetup attach cryptroot /dev/sda2 - tpm2-device=auto
Если корень лежит на LVM-томе, здесь активируются volume group:
vgchange -a y
Если используется программный RAID, собирается массив:
mdadm --assemble --scan
После всех этих операций корневая файловая система наконец готова к монтированию. Она монтируется в /sysroot или /mnt/sysroot.
Переключение корня и передача управления
Последний шаг в initramfs называется switch_root или pivot_root. Это тот момент, когда временная файловая система уступает место настоящей.
switch_root работает так: процесс меняет свой корень (/) с tmpfs на только что смонтированный /sysroot, после чего execv() запускает /sbin/init или /usr/lib/systemd/systemd уже из настоящей файловой системы. Временный init при этом завершается, tmpfs освобождается из памяти. Именно в этот момент PID 1 в настоящей системе получает управление и начинается уже знакомый процесс: инициализация юнитов systemd, монтирование разделов из fstab, запуск сервисов.
Посмотреть, что происходит на этом переходе в реальном времени, можно добавив в командную строку ядра параметр rd.break=pre-pivot. Система остановится прямо перед переключением корня и предоставит оболочку внутри initramfs:
# корень всё ещё tmpfs, настоящая ФС смонтирована в /sysroot
ls /sysroot/etc
chroot /sysroot
Это же является стандартным способом восстановления пароля root или починки сломанной системы без LiveCD.
Как создаётся initramfs и инструменты для этого
Образ initramfs генерируется автоматически при установке или обновлении ядра. Три основных инструмента выполняют эту задачу по-разному.
Dracut, используемый в Fedora, RHEL, openSUSE и многих других дистрибутивах, анализирует текущую систему и сам определяет, какие модули и инструменты включить. В режиме hostonly он создаёт минимальный образ именно для той машины, на которой запущен. Базовые команды:
# пересобрать для текущего ядра
dracut --force
# пересобрать для конкретного ядра
dracut --force /boot/initramfs-6.12.0.img 6.12.0
# режим hostonly (минимальный образ)
dracut -H --force
# пересобрать для всех установленных ядер
dracut --regenerate-all --force
mkinitcpio, родной инструмент Arch Linux, работает на основе хуков: bash-скриптов, выполняемых в заданном порядке. Конфигурация находится в /etc/mkinitcpio.conf, где строка HOOKS определяет, какие компоненты войдут в образ:
HOOKS=(base udev autodetect modconf block encrypt lvm2 filesystems keyboard fsck)
Пересборка всех образов:
mkinitcpio -P
update-initramfs, инструмент Debian и Ubuntu, является обёрткой над initramfs-tools:
# обновить для текущего ядра
update-initramfs -u
# обновить для всех ядер
update-initramfs -u -k all
Отладка загрузки и типичные проблемы
Когда система не загружается и зависает в initramfs с сообщением типа "Gave up waiting for root device", это означает, что временный init не смог найти или смонтировать корневую файловую систему. Причины бывают разные: неправильный UUID в параметре root=, отсутствующий модуль драйвера, сломанный LUKS-заголовок.
Для диагностики в командную строку ядра добавляют rd.debug, что включает подробный вывод dracut:
rd.debug rd.break=pre-mount
Параметр rd.break останавливает загрузку в нужной точке. Допустимые значения: cmdline, pre-udev, pre-trigger, initqueue, pre-mount, mount, pre-pivot, cleanup. Каждая точка соответствует определённой фазе работы initramfs.
Проверить, что конкретный модуль есть в образе:
lsinitrd /boot/initramfs-$(uname -r).img | grep ahci
Если модуль отсутствует, добавить его в dracut можно через файл конфигурации:
echo 'add_drivers+=" ahci "' > /etc/dracut.conf.d/ahci.conf
dracut --force
Разрыв между нажатием кнопки питания и появлением systemd в пользовательском пространстве занимает секунды, но вмещает в себя полноценный жизненный цикл отдельной операционной среды. Initramfs рождается, выполняет свою единственную задачу и исчезает из памяти, не оставив следов. Именно благодаря этой невидимой работе ядро Linux с модульными драйверами, шифрованием и сложными дисковыми конфигурациями вообще способно загрузиться на любом железе без перекомпиляции под каждую конкретную машину.