systemd как init: targets и порядок
systemd как init: targets, unit-файлы, параллельная загрузка.
Кто такой PID 1
К этому моменту цепочка загрузки уже прошла: firmware -> загрузчик ->
ядро -> initramfs. Ядро распаковало initramfs в rootfs на tmpfs (при
включённом CONFIG_TMPFS), нашло настоящий корень, смонтировало его и
передало управление первому пользовательскому процессу.
Этот процесс — systemd, получивший PID 1. systemd — демон, который
управляет остальными демонами. Демон здесь означает фоновую службу без
терминала: sshd, cron, сетевой стек. systemd их запускает, следит за
ними и перезапускает при падении.
$ ps -p 1 -o pid,comm
PID COMMAND
1 systemd
Unit-файлы вместо скриптов
Инструкции инициализации у systemd лежат в декларативных unit-файлах, а не в shell-скриптах. Декларативный означает, что файл описывает желаемое состояние (что запустить, после чего, при каких условиях), а не последовательность команд, которую надо выполнить сверху вниз.
Один unit — одна сущность: служба, точка монтирования, сокет, таймер или цель. Посмотреть загруженные юниты можно так:
systemctl list-units --type=service # активные службы
systemctl status sshd # состояние конкретной службы
Как устроен сам unit-файл, из каких секций собран и что значит каждое поле, подробно разбирается в уроке linux-admin/systemd-units. Здесь мы на этом не останавливаемся.
Targets: состояния системы
Целое состояние системы systemd описывает целью. Target — это именованная группа юнитов, которую нужно привести в активное состояние. Достижение цели значит, что все её зависимости запущены.
Несколько целей, которые встречаются постоянно:
| Target | Что означает |
|---|---|
sysinit.target | базовая инициализация: монтирование, swap, journald |
basic.target | сокеты, таймеры, пути — фундамент для служб |
multi-user.target | серверный режим, службы без графики |
graphical.target | всё из multi-user плюс графическая сессия |
Какая цель включается по умолчанию, задаётся отдельно:
$ systemctl get-default
multi-user.target
Список загруженных целей смотрят так:
$ systemctl list-units --type=target
UNIT STATE SUB DESCRIPTION
basic.target loaded active Basic System
multi-user.target loaded active Multi-User System
sysinit.target loaded active System Initialization
Порядок задаётся графом, а не очередью
Вот ключевой момент. graphical.target тянет multi-user.target, тот
тянет basic.target, а тот тянет sysinit.target. Эта цепочка зависимостей
соблюдается: графическая сессия не поднимется раньше базовой
инициализации.
Но это не строгая последовательная очередь. Загрузка сильно параллельна: systemd строит граф зависимостей и запускает всё, что уже может стартовать, одновременно. Порядок между двумя независимыми юнитами недетерминирован. Сегодня первым поднимется один, завтра другой.
$ systemctl list-dependencies graphical.target
graphical.target
* |-multi-user.target
* | |-basic.target
* | | |-sysinit.target
Отступ в выводе показывает зависимости, а не время старта. Юниты внутри одного уровня стартуют параллельно.
Две частые ловушки
Первая: «targets идут строго друг за другом, как ступеньки». Нет. Зависимость упорядочивает только связанные юниты. Всё остальное systemd запускает так параллельно, как позволяет граф. Насколько параллельно прошла загрузка, видно здесь:
$ systemd-analyze
Startup finished in 2.1s (kernel) + 4.8s (userspace) = 6.9s
graphical.target reached after 4.7s in userspace
Вторая: «target это то же самое, что runlevel, один в один». Похожая идея (уровень работы системы), но не совпадение. Runlevel был жёстким номером с фиксированным набором скриптов. Target — узел в графе зависимостей, целей может быть сколько угодно, и своя цель определяется через unit-файлы, а не через номер.
systemctl isolate multi-user.target # переключить систему в серверный режим
systemctl isolate graphical.target # поднять графическую сессию
Что важно запомнить
Первый пользовательский процесс — systemd, PID 1, демон над демонами.
Инструкции инициализации живут в декларативных unit-файлах, не в shell-скриптах.
Target — именованная группа юнитов; multi-user.target без графики,
graphical.target с ней. Дефолт смотрят через systemctl get-default.
Цель достигается по графу зависимостей. Цепочка graphical -> multi-user -> basic -> sysinit соблюдается, но загрузка сильно параллельна и порядок независимых юнитов недетерминирован.
Target решает ту же задачу, что runlevel в старых init-системах, но устроен иначе: зависимости вместо номера уровня.
В следующем уроке — udev: как система обнаруживает устройства и наполняет /dev.
Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс