Процесс загрузки Linux: от питания до пользовательского пространства

Академически строгий курс по этапам загрузки Linux для DevOps-инженеров и технических собеседований. Охватывает полную цепочку: BIOS/UEFI, GRUB, инициализацию ядра, монтирование rootfs и работу systemd.

1. Этап BIOS и UEFI: инициализация оборудования и поиск загрузчика

Этап BIOS и UEFI: инициализация оборудования и поиск загрузчика

Представьте: вы нажимаете кнопку питания сервера в дата-центре, а через 40 секунд монитор остаётся чёрным. Ни ошибки, ни лога — ничего. Проблема может быть где угодно, но диагностику придётся начинать с самого первого звена цепочки загрузки: прошивки материнской платы. Именно поэтому DevOps-инженеру критически важно понимать, что происходит в первые миллисекунды после подачи питания — до того, как ядро Linux вообще начнёт загружаться.

POST: самотестирование при включении

Когда питание поступает на материнскую плату, процессор начинает выполнять инструкции из прошивки — программного кода, записанного в энергонезависимую память микросхемы на плате. Первое, что делает прошивка — запускает процедуру POST (Power-On Self-Test), самотестирование при включении.

POST проверяет базовую работоспособность оборудования:

  • целостность оперативной памяти (RAM);
  • обнаружение и инициализация видеокарты;
  • определение подключённых накопителей (HDD, SSD, NVMe);
  • проверка клавиатуры и мыши;
  • конфигурация устройств Plug and Play.
  • Если POST проходит успешно, на старых системах раздавался один короткий пик — так называемый beep code. Если что-то не так — комбинация звуковых сигналов указывала на конкретную неисправность. На современных серверах вместо beep codes используются POST-коды, выводимые на цифровой индикатор материнской платы или в IPMI/BMC-лог.

    > POST — это не «проверка Linux». Это аппаратная процедура, выполняемая прошивкой до загрузки любой операционной системы. Если POST не проходит, до загрузчика дело не дойдёт.

    BIOS: классический подход

    BIOS (Basic Input/Output System) — прошивка, которая десятилетиями была стандартом для персональных компьютеров и серверов. BIOS хранится в микросхеме flash memory на материнской плате и выполняет несколько ключевых функций.

    После завершения POST BIOS обращается к таблице приоритетов загрузки (Boot Order) — упорядоченному списку устройств, с которых система попытается загрузиться. Типичный порядок: CD/DVD, USB, жёсткий диск, сетевая карта (PXE boot). BIOS последовательно проверяет каждое устройство: читает первый сектор, ищет сигнатуру загрузчика (байты 0x55AA в конце 512-байтового сектора) и, если находит, передаёт управление коду из этого сектора.

    Ключевые ограничения BIOS:

  • работает с таблицей разделов MBR (Master Boot Record), которая поддерживает не более 4 первичных разделов и диски объёмом до 2 ТБ;
  • загрузочный код MBR занимает всего 446 байт — этого хватает лишь на минимальную программу, которая знает, где на диске лежит следующий этап загрузчика;
  • не имеет встроенной поддержки сетевых протоколов, шифрования или графического интерфейса.
  • Проверить, в каком режиме загрузилась система, можно командой:

    UEFI: современная замена

    UEFI (Unified Extensible Firmware Interface) — спецификация прошивки, пришедшая на смену BIOS. UEFI принципиально отличается архитектурно и устраняет большинство ограничений предшественника.

    | Характеристика | BIOS | UEFI | |---|---|---| | Таблица разделов | MBR (до 2 ТБ, 4 раздела) | GPT (до 9.4 ЗБ, 128 разделов) | | Загрузочный код | 446 байт в MBR | Отдельный EFI System Partition (FAT32) | | Графический интерфейс | Текстовый, клавиатура | Графический, мышь | | Безопасная загрузка | Нет | Secure Boot (проверка подписи) | | Сетевая загрузка | Через PXE-расширение | Встроенный сетевой стек | | Архитектура | 16-битный реальный режим | 32/64-битный защищённый режим |

    EFI System Partition (ESP) — специальный раздел на диске, отформатированный в FAT32, на котором хранятся загрузочные файлы .efi. Каждая ОС устанавливает свой загрузочный файл в ESP, а UEFI-прошивка выбирает нужный на основе NVRAM-переменных — настроек, хранящихся в энергонезависимой памяти прошивки.

    Посмотреть смонтированный ESP и его содержимое:

    Обычно внутри вы увидите директории вроде ubuntu/, debian/ или BOOT/, в которых лежат .efi-файлы загрузчиков.

    Secure Boot: защита от вредоносного кода

    Одна из самых обсуждаемых возможностей UEFI — Secure Boot. Механизм работает так: прошивка хранит набор доверенных сертификатов (Platform Key, Key Exchange Key, Authorized/Forbidden Signature Databases). При загрузке любого .efi-файла прошивка проверяет его цифровую подпись. Если подпись не проходит проверку — загрузка прерывается.

    Для Linux это исторически создавало проблемы: дистрибутивы должны были либо получать сертификаты от Microsoft (которая владеет ключом, предустановленным на большинстве плат), либо поставлять свои ключи для ручной установки. Сегодня большинство крупных дистрибутивов (Ubuntu, Fedora, Debian) подписывают свои загрузчики и ядра, поэтому Secure Boot работает «из коробки».

    > На практике: если вы настраиваете сервер с кастомным ядром или загрузчиком, Secure Boot может блокировать загрузку. В таком случае либо подпишите ядро через sbsign, либо отключите Secure Boot в настройках UEFI — но только если вы понимаете риски.

    Legacy vs UEFI: какой режим выбрать

    На большинстве современных серверов UEFI — единственный доступный режим. Но в виртуализации и контейнеризации ситуация может быть иной: гипервизоры (KVM, VMware) поддерживают оба режима, а в некоторых сценариях PXE boot Legacy-режим проще настраивать.

    Для DevOps-инженера практическое правило такое: если система поддерживает UEFI — используйте UEFI с GPT. Это даёт больше гибкости, поддержку дисков ТБ и возможность использовать Secure Boot. Legacy BIOS оставляйте только для совместимости со старым оборудованием или специфическими bare-metal-сценариями.

    Что происходит в момент передачи управления

    Когда прошивка (BIOS или UEFI) находит загрузочный сектор или .efi-файл, она загружает его код в оперативную память и передаёт управление, выполняя jump на точку входа этого кода. На этом этапе прошивка завершает свою роль — дальше работает загрузчик, о котором пойдёт речь в следующей статье.

    Важный нюанс: в режиме Legacy BIOS процессор в момент передачи управления загрузчику находится в 16-битном реальном режиме — том же режиме, в котором работал оригинальный Intel 8086. Загрузчику предстоит выполнить серию переключений процессора в защищённый, а затем в длинный режим, прежде чем можно будет запустить 64-битное ядро Linux. В UEFI этот переход уже частично выполнен прошивкой, что упрощает жизнь загрузчику.

    2. Загрузчик GRUB: конфигурация, меню и передача управления ядру

    Загрузчик GRUB: конфигурация, меню и передача управления ядру

    Когда прошивка BIOS/UEFI нашла загрузочный сектор и передала управление, в дело вступает программа, которая занимает промежуточное положение между «голым железом» и ядром операционной системы. Если прошивка — это швейцар, открывающий дверь в здание, то загрузчик — лифтёр, который доставляет вас на нужный этаж. Без него ядро не знает, где на диске лежит файл образа, какие параметры ему передать и какую версию загрузить. В мире Linux этим лифтером является GRUBGRand Unified Bootloader.

    Три стадии GRUB в Legacy-режиме

    В режиме Legacy BIOS загрузчик GRUB2 работает в три этапа, каждый из которых решает конкретную задачу, обусловленную ограничениями предыдущего этапа.

    Stage 1 — код, записанный в первые 446 байт MBR. Его размер жёстко ограничен, поэтому он содержит лишь указатель на местоположение Stage 1.5. По сути, это «записка»: «меня мало, но я знаю, где лежит продолжение».

    Stage 1.5 — промежуточный код, расположенный в секторах сразу за MBR (так называемая post-MBR gap — пространство между MBR и первым разделом). Именно здесь GRUB получает возможность читать файловые системы. Stage 1.5 содержит миниатюрные драйверы файловых систем (ext2/3/4, XFS, Btrfs), достаточные для того, чтобы найти и прочитать файлы Stage 2.

    Stage 2 — основной код загрузчика, который располагается в /boot/grub/ на загрузочном разделе. Stage 2 отображает меню выбора ОС, читает конфигурацию, загружает ядро и initramfs в оперативную память.

    > Аналогия: Stage 1 — это как указатель «конференц-зал на 3 этаже», Stage 1.5 — лифт, который умеет читать таблички на дверях, а Stage 2 — навигатор, который приводит вас точно в нужный кабинет.

    GRUB в режиме UEFI

    В UEFI-режиме схема упрощается. Вместо трёх стадий GRUB устанавливается как один файл .efi в EFI System Partition (например, /boot/efi/EFI/ubuntu/grubx64.efi). UEFI-прошивка загружает этот файл напрямую, минуя MBR и Stage 1/1.5. Файлы конфигурации и модули GRUB при этом могут лежать на отдельном /boot-разделе, а путь к ним указывается в самом .efi-образе.

    Проверить, какой загрузчик используется:

    Команда покажет список загрузочных записей UEFI с указанием путей к .efi-файлам.

    Конфигурация GRUB: grub.cfg

    Главный конфигурационный файл GRUB — /boot/grub/grub.cfg. Это файл, который GRUB читает при загрузке и на основе которого строит меню выбора. Вот типичная структура одной записи:

    Ключевые строки здесь:

  • insmod — загружает модули GRUB (поддержка GPT, ext2, сжатие gzio);
  • search --set=root — ищет раздел по UUID и устанавливает его как корневой для GRUB;
  • linux — указывает путь к образу ядра (vmlinuz) и параметры ядра (kernel parameters), передаваемые через строку команды;
  • initrd — указывает путь к образу initramfs, который будет загружен вместе с ядром.
  • > Никогда не редактируйте grub.cfg вручную для постоянных изменений — при обновлении ядра или вызове update-grub ваши правки будут перезаписаны. Используйте /etc/default/grub и файлы в /etc/grub.d/.

    Параметры ядра в GRUB

    Строка linux /vmlinuz-... root=UUID=... ro quiet splash — это не просто путь к файлу. Всё, что идёт после имени файла ядра — это параметры ядра (kernel command line), которые определяют поведение системы на ранних этапах загрузки.

    Наиболее важные параметры:

    | Параметр | Назначение | |---|---| | root=UUID=... | Указывает корневую файловую систему | | ro | Монтировать корневую ФС в режиме «только чтение» на старте | | quiet | Подавить вывод сообщений ядра (только критические ошибки) | | splash | Показывать splash screen (обычно через Plymouth) | | single или init=/bin/bash | Загрузиться в однопользовательском режиме (для восстановления) | | nomodeset | Не загружать графические драйверы (спасает при проблемах с GPU) | | systemd.unit=rescue.target | Загрузиться в режиме восстановления systemd |

    Для диагностики проблем с загрузкой часто полезно убрать quiet splash — тогда вы увидите все сообщения ядра и systemd прямо на экране.

    Меню GRUB и его настройка

    Файл /etc/default/grub — основной файл настроек, из которого генерируется grub.cfg:

  • GRUB_DEFAULT — какой пункт меню выбирать по умолчанию (0 — первый);
  • GRUB_TIMEOUT — таймаут в секундах до автоматической загрузки;
  • GRUB_CMDLINE_LINUX_DEFAULT — параметры ядра, добавляемые ко всем записям;
  • GRUB_CMDLINE_LINUX — параметры, добавляемые ко всем записям, включая режим восстановления.
  • После изменения /etc/default/grub необходимо перегенерировать конфигурацию:

    Скрытый таймаут и аварийный доступ

    Если GRUB-меню не появляется (система загружается мгновенно), удерживайте Shift (BIOS) или нажмите Esc (UEFI) во время POST. Это принудительно откроет меню GRUB. В критических ситуациях — когда система не загружается вообще — можно отредактировать параметры ядра прямо в меню GRUB, нажав e на выбранном пункте. Это позволяет, например, добавить init=/bin/bash для получения root-шелла без пароля.

    Передача управления ядру

    Когда пользователь выбирает пункт меню (или срабатывает таймаут), GRUB выполняет две операции: загружает файл ядра (vmlinuz) в оперативную память и загружает образ initramfs (initrd.img) рядом с ним. После этого GRUB выполняет инструкцию jmp — передаёт управление точке входа ядра. На этом роль загрузчика завершается: он больше не участвует в работе системы.

    3. Инициализация ядра Linux: распаковка, настройка памяти и запуск start_kernel

    Инициализация ядра Linux: распаковка, настройка памяти и запуск start_kernel

    Когда GRUB передаёт управление точке входа ядра, процессор всё ещё работает в 16-битном реальном режиме (в случае Legacy BIOS) или в защищённом режиме, установленном UEFI. Но ядро Linux — это полноценная 64-битная программа. Между текущим состоянием процессора и запуском полноценного ядра лежит целая серия переключений режимов, распаковок и инициализаций, о которых большинство инженеров даже не задумывается. Именно этот этап — «чёрный ящик» между GRUB и первыми сообщениями в логе — часто становится источником проблем, которые невозможно диагностировать без понимания внутреннего устройства.

    Формат образа ядра: не просто файл

    Файл vmlinuz (или vmlinux), который GRUB загружает в память — это не обычный ELF-бинарник. Образ ядра имеет собственный формат, состоящий из нескольких частей:

  • Заголовок загрузчика (boot header) — метаданные, которые GRUB читает для понимания, как загружать ядро: адрес загрузки, размер, поддерживаемые протоколы загрузки.
  • Защищённый режим-код (protected mode setup) — небольшой код, который переводит процессор из 16-битного реального режима в 32-битный защищённый, а затем в 64-битный длинный режим.
  • Сжатый образ ядра — основная часть, сжатая алгоритмом (gzip, bzip2, LZMA, XZ, LZ4 или Zstd в зависимости от конфигурации).
  • Почему ядро хранится в сжатом виде? Несжатое ядро современного дистрибутива может занимать 20–50 МБ. Сжатие уменьшает объём в 3–5 раз, что критично для скорости загрузки с медленных накопителей.

    Процесс распаковки

    После передачи управления от GRUB первое, что делает код ядра — распаковывает себя. Это происходит в несколько этапов:

  • Код bootstrap (написанный на ассемблере) выполняет базовую инициализацию: настраивает стек, проверяет CPUID, определяет поддержку 64-битного режима.
  • Переключает процессор в защищённый режим, затем в длинный режим (long mode).
  • Вызывает декомпрессор — программу на C, которая распаковывает сжатый образ ядра в заранее определённый адрес в оперативной памяти.
  • Передаёт управление распакованному ядру.
  • Распаковка выполняется in-place: сжатые данные перезаписываются распакованными. Декомпрессор знает, что распакованный образ не перезапишет сам себя, потому что сжатый образ всегда больше распакованного (в смысле занимаемого адресного пространства — данные сжимаются, но адреса растут).

    > Практический момент: если вы компилируете ядро вручную и выбираете неподдерживаемый GRUB алгоритм сжатия (например, Zstd в старой версии GRUB), загрузчик не сможет передать управление ядру. Всегда проверяйте совместимость.

    start_kernel(): точка входа в ядро

    После распаковки управление попадает в функцию start_kernel(), расположенную в файле init/main.c исходного кода ядра. Эта функция — главная точка инициализации: именно она превращает набор распакованных байтов в работающую операционную систему.

    start_kernel() вызывает десятки функций инициализации в строгом порядке. Вот ключевые этапы:

    Разберём самые важные вызовы:

  • set_task_stack_end_magic() — инициализирует структуру первого процесса ядра (init_task, PID 0, он же swapper);
  • mm_init() — настраивает подсистему управления памятью: выделяет области для slab-аллокатора, инициализирует page tables;
  • sched_init() — запускает планировщик задач, который будет распределять процессорное время между процессами;
  • trap_init() — настраивает таблицу обработчиков прерываний и исключений (IDT — Interrupt Descriptor Table);
  • console_init() — инициализирует консоль ядра, после чего на экране появляются первые сообщения;
  • vfs_caches_init() — инициализирует виртуальную файловую систему (VFS), которая является абстракцией для всех файловых систем Linux.
  • rest_init() и рождение PID 1

    Финальный вызов в start_kernel()rest_init(). Эта функция создаёт два процесса:

  • kernel_init (PID 1) — процесс, который впоследствии станет /sbin/init (systemd). На этом этапе он ещё выполняется в контексте ядра.
  • kthreadd (PID 2) — демон ядра, который управляет потоками ядра (kernel threads).
  • После создания этих процессов исходный поток start_kernel() превращается в процесс swapper (PID 0), который будет работать, когда нет других задач для выполнения — по сути, это «холостой ход» процессора.

    > На интервью часто спрашивают: «Почему PID 1 — это init, а не ядро?» Ответ: ядро не является процессом в классическом смысле. PID 0 (swapper) — это «процесс» планировщика, а PID 1 — первый процесс пользовательского пространства, который ядро создаёт для запуска системы инициализации.

    Раннее пространство пользователя: роль initramfs

    На момент вызова rest_init() ядро ещё не смонтировало реальную корневую файловую систему. Почему? Потому что драйверы для доступа к корневому диску (например, драйвер RAID-контроллера, LVM, LUKS-шифрование) могут быть скомпилированы как модули и лежать на самом диске — это классическая проблема «курицы и яйца».

    Для её решения ядро использует initramfs — временную файловую систему, загруженную GRUB в оперативную память вместе с ядром. Процесс kernel_init первым делом распаковывает initramfs и выполняет скрипт /init из него. Этот скрипт загружает необходимые модули, настраивает доступ к реальному корневому диску и выполняет switch_root — переключение на настоящую корневую файловую систему.

    Детальная работа initramfs — тема следующей статьи.

    Сообщения ядра: dmesg

    Все этапы инициализации ядра фиксируются в кольцевом буфере сообщений (kernel ring buffer). Посмотреть их можно командой:

    Первые строки dmesg показывают инициализацию CPU, обнаружение памяти, настройку прерываний. Последующие — загрузку драйверов, обнаружение устройств, монтирование файловых систем. Именно по dmesg диагностируют проблемы на ранних этапах загрузки: отсутствие драйвера, ошибки памяти, конфликты устройств.

    4. Монтирование корневой файловой системы: initramfs, udev и переход к rootfs

    Монтирование корневой файловой системы: initramfs, udev и переход к rootfs

    После того как ядро завершило базовую инициализацию и создало процесс kernel_init (PID 1), оно сталкивается с фундаментальной проблемой: корневая файловая система, на которой лежат все программы, конфигурации и драйверы, находится на диске, к которому ядро пока не может обратиться. Драйверы контроллера диска, модули шифрования, логический менеджер томов — всё это либо скомпилировано как модуль и лежит на том самом недоступном диске, либо требует специфической настройки до монтирования. Эту проблему «курицы и яйца» решает initramfs — временная файловая система, живущая целиком в оперативной памяти.

    initramfs vs initrd: историческая справка

    В ранних версиях Linux использовался initrd (initial RAM disk) — блочное устройство в памяти, на которое монтировался образ файловой системы (обычно ext2). Ядро создавало виртуальный блочный устройство /dev/ram0, копировало туда образ и монтировало его. Это работало, но было неэффективно: initrd требовал фиксированный размер, выделял память с запасом, а при обновлении образа нужно было пересоздавать файловую систему.

    Современный initramfs (initial RAM filesystem) — принципиально иной механизм. Это cpio-архив (иногда сжатый gzip или zstd), который ядро распаковывает непосредственно в tmpfs — файловую систему, полностью расположенную в оперативной памяти. Нет виртуального блочного устройства, нет фиксированного размера, нет лишнего копирования.

    Посмотреть, какой initramfs используется системой:

    Процесс работы скрипта /init

    Когда ядро распаковывает initramfs в tmpfs, оно ищет и запускает файл /init. Этот скрипт (или бинарник, в случае dracut или mkinitcpio) выполняет последовательность действий:

  • Монтирует виртуальные ФС: /proc, /sys, /dev — необходимы для взаимодействия с ядром и обнаружения устройств.
  • Загружает модули ядра: драйверы контроллеров дисков, файловых систем, модули шифрования (dm-crypt), модули LVM.
  • Обнаруживает корневое устройство: использует информацию из параметров ядра (root=UUID=... или root=/dev/sda1).
  • Проверяет и монтирует корневую ФС: выполняет fsck, монтирует в /sysroot (или /newroot).
  • Выполняет switch_root: переключает корневую файловую систему с initramfs на реальную и запускает /sbin/init из неё.
  • > Ключевой момент: switch_root — это не просто mount. Он удаляет содержимое initramfs из памяти и делает реальную ФС новым корнем. После switch_root initramfs перестаёт существовать, и память, которую он занимал, освобождается.

    udev: обнаружение устройств

    В процессе работы initramfs критическую роль играет udev — демон управления устройствами. Когда ядро обнаруживает новое оборудование (USB-накопитель, сетевую карту, блочное устройство), оно генерирует событие uevent. Демон udev перехватывает это событие и:

  • создаёт файл устройства в /dev/ (например, /dev/sda1);
  • загружает соответствующий модуль ядра, если он ещё не загружен;
  • применяет правила именования (например, predictable network interface names: enp0s3 вместо eth0);
  • запускает пользовательские скрипты из /etc/udev/rules.d/.
  • В контексте initramfs udev (или его полегче аналог eudev) необходим для обнаружения корневого диска. Без него ядро может «видеть» диск на уровне PCI, но файл устройства в /dev/ не появится.

    Проверить события udev в реальном времени:

    Проблемы с initramfs на практике

    Наиболее частые сценарии, когда initramfs становится причиной незагружающейся системы:

    Отсутствует драйвер. Вы добавили новый контроллер диска (например, перенесли VM на другой гипервизор), но в initramfs нет соответствующего модуля. Решение: пересобрать initramfs с нужными модулями:

    Повреждённый образ. Если initramfs повреждён, ядро не сможет найти корневую ФС. Решение: загрузиться с Live-USB, смонтировать корневой раздел и пересобрать initramfs.

    Шифрованный диск (LUKS). При использовании LUKS initramfs запрашивает пароль для расшифровки. Если скрипт /init не настроен на запрос пароля или не загружен модуль dm-crypt, система зависнет на этапе монтирования.

    Переход к реальной корневой ФС

    После успешного switch_root ядро получает доступ к полноценной файловой системе со всеми программами, библиотеками и конфигурациями. Процесс kernel_init (PID 1) теперь ищет программу инициализации — в современных системах это /sbin/systemd (или /sbin/init, который является символической ссылкой на systemd).

    Именно с этого момента начинается финальный этап загрузки — запуск системы инициализации, которому посвящена следующая статья.

    5. Системы инициализации: systemd, юниты, уровни запуска и старт процессов

    Системы инициализации: systemd, юниты, уровни запуска и старт процессов

    Когда ядро завершило инициализацию и выполнило switch_root на реальную корневую файловую систему, оно запускает программу /sbin/init — процесс с PID 1, который становится родителем для всех последующих пользовательских процессов. Именно этот процесс определяет, какие службы запустятся, в каком порядке, с какими зависимостями и в каком состоянии окажется система после загрузки. В современном Linux эту роль выполняет systemd — и понимание его устройства критически важно для любого DevOps-инженера, которому приходится диагностировать проблемы запуска сервисов, настраивать автозагрузку контейнеров или отлаживать boot time сервера.

    От SysV init к systemd: зачем понадобилась замена

    Классическая система инициализации SysV init работала по простому принципу: скрипты в /etc/init.d/ запускались последовательно, в порядке, определяемом числовыми префиксами имён. Скрипт S10network запускался раньше S20sshd, потому что 10 < 20. Это было предсказуемо, но медленно: на сервере с десятками служб загрузка занимала минуты, потому что каждая служба ждала завершения предыдущей, даже если между ними не было никакой зависимости.

    systemd решил эту проблему тремя ключевыми идеями:

  • параллелизм: службы, не зависящие друг от друга, запускаются одновременно;
  • декларативность: зависимости описываются явно в конфигурационных файлах, а не порядком имён;
  • управление состоянием: systemd отслеживает не только «запущен/остановлен», но и готовность службы к работе.
  • Юниты: атомарные единицы systemd

    Всё, чем управляет systemd, представлено в виде юнитов (units) — конфигурационных файлов с расширением, определяющим тип. Юниты хранятся в нескольких директориях, и порядок их приоритета строго определён:

    | Директория | Приоритет | Назначение | |---|---|---| | /etc/systemd/system/ | Высший | Администраторские переопределения | | /run/systemd/system/ | Средний | Рантайм-конфигурации | | /lib/systemd/system/ | Низший | Конфигурации по умолчанию из пакетов |

    Основные типы юнитов:

  • .service — демон или служба (nginx, sshd, docker);
  • .socket — сокет для socket activation (запуск службы по первому подключению);
  • .target — группа юнитов, аналог runlevel (multi-user.target, graphical.target);
  • .mount — точка монтирования файловой системы;
  • .timer — расписание запуска (замена cron);
  • .path — мониторинг файловой системы (запуск службы при изменении файла).
  • Посмотреть все загруженные юниты:

    Структура service-юнита

    Разберём типичный юнит на примере SSH-демона (/lib/systemd/system/ssh.service):

    Секция [Unit] описывает метаданные и зависимости:

  • After=network.target — запустить после поднятия сети (порядок, не жёсткая зависимость);
  • Wants=network.target — мягкая зависимость: если сеть не поднялась, sshd всё равно попробует запуститься.
  • Секция [Service] описывает поведение службы:

  • Type=forking — процесс демонизируется (создаёт дочерний процесс и завершает родительский);
  • ExecStart — команда запуска;
  • Restart=on-failure — автоматический перезапуск при падении;
  • RestartSec=42s — задержка перед перезапуском.
  • Секция [Install] определяет, в каком target будет включена служба при systemctl enable.

    > Типы процессов в systemd — частый вопрос на собеседовании. simple — процесс на переднем плане, PID совпадает с запущенной командой. forking — процесс демонизируется. oneshot — выполняется и завершается (скрипты). notify — процесс сообщает о готовности через sd_notify().

    Таргеты: замена runlevel

    В SysV init использовались уровни запуска (runlevels) — числа от 0 до 6, определяющие состояние системы:

    | Runlevel | Назначение | |---|---| | 0 | Выключение | | 1 | Однопользовательский режим | | 2 | Многопользовательский без сети | | 3 | Многопользовательский с сетью | | 5 | Многопользовательский с графическим интерфейсом | | 6 | Перезагрузка |

    В systemd таргеты (targets) выполняют ту же роль, но с важным отличием: таргет может включать в себя другие таргеты и юниты, формируя иерархию зависимостей.

    Основные таргеты и их соответствие:

    | Таргет systemd | Аналог runlevel | Описание | |---|---|---| | poweroff.target | 0 | Выключение | | rescue.target | 1 | Однопользовательский режим | | multi-user.target | 3 | Текстовый многопользовательский | | graphical.target | 5 | Графический многопользовательский | | reboot.target | 6 | Перезагрузка |

    Посмотреть текущий таргет:

    Установить таргет по умолчанию:

    Порядок загрузки systemd

    При старте systemd выполняет следующую последовательность:

  • Чтение конфигурации: systemd определяет таргет по умолчанию из /etc/systemd/system/default.target (обычно символическая ссылка на graphical.target или multi-user.target).
  • Построение графа зависимостей: на основе директив Requires, Wants, After, Before в юнитах строится ориентированный ациклический граф (DAG) зависимостей.
  • Параллельный запуск: все юниты, чьи зависимости удовлетворены, запускаются одновременно. Остальные ждут разрешения своих зависимостей.
  • Переход к целевому таргету: когда все юниты целевого таргета (и их транзитивные зависимости) запущены, система считается загруженной.
  • Измерить время загрузки:

    Команда blame покажет, какие службы стартовали дольше всего — это первый шаг при оптимизации времени загрузки.

    Управление службами: практические команды

    Для DevOps-инженера базовый набор команд systemd — это ежедневный инструмент:

    > Важный нюанс: systemctl enable не запускает службу — он создаёт символическую ссылку из /etc/systemd/system/multi-user.target.wants/ на юнит, что гарантирует запуск при следующей загрузке. Чтобы включить и сразу запустить: sudo systemctl enable --now nginx.

    Зависимости между юнитами: Requires, Wants, After

    Понимание директив зависимостей — ключ к диагностике проблем автозагрузки:

  • Requires=foo.service — жёсткая зависимость: если foo не запустился, текущий юнит тоже не запустится;
  • Wants=foo.service — мягкая зависимость: попытаться запустить foo, но если не получилось — не страшно;
  • After=foo.service — порядок: запустить после foo, но не требовать его обязательного запуска;
  • Before=foo.service — обратный порядок;
  • Conflicts=foo.service — взаимоисключение: если foo запущен, остановить его перед запуском текущего юнита.
  • На практике это означает, что если служба не стартует при загрузке, но работает при ручном systemctl start, проблема, скорее всего, в неудовлетворённой зависимости. Проверить можно так:

    systemd в контексте контейнеров и оркестрации

    Для DevOps-инженера systemd выходит за рамки загрузки ОС. В Docker-контейнерах systemd часто используется как PID 1 для запуска нескольких процессов (хотя это и не рекомендуется — лучше один процесс на контейнер). В Kubernetes systemd управляет kubelet на нодах. Понимание того, как systemd обрабатывает сигналы, управляет cgroup и отслеживает процессы, помогает диагностировать «зомби-процессы», утечки памяти и проблемы с graceful shutdown подов.