Перейти к содержанию

systemd

systemd - PID 1, то есть первый процесс, который запускает ядро Linux. Он поднимает всю остальную систему и дальше управляет ею. Вокруг PID 1 выросло целое семейство системной обвязки: журнал, управление устройствами, сессиями пользователей, сетью, DNS, временем, загрузчиком.

Note

Отсюда главная путаница в спорах. «systemd» называют то init-систему (один бинарник), то весь проект из десятков компонентов - фактически новый слой «Linux userspace plumbing» между ядром и приложениями.

Название - игра слов: d - стандартный суффикс демонов (sshd, httpd), а Système D - французское выражение про умение выкрутиться в любой ситуации.

См. systemd: юнит сервиса.


Предыстория: init до systemd

SysV init (1983-2010)

В Linux пришла модель из UNIX System V (AT&T, 1983). Linux-реализацию sysvinit написал Миквел ван Смуренбург в 1992 году.

  • /etc/inittab и runlevels: 0 - выключение, 3 - многопользовательский режим, 5 - графика, 6 - перезагрузка
  • /etc/init.d/ - shell-скрипты с аргументами start | stop | restart | status
  • /etc/rc3.d/ - ссылки на них вида S20network, S80nginx, номер в имени задаёт порядок запуска

Двадцать лет это работало, но к концу 2000-х проблемы стали очевидны:

  1. Всё последовательно. Сервис 80 ждёт сервис 20, даже если они никак не связаны. На ноутбуках загрузка занимала минуты.
  2. Init не знает, что с сервисом. Скрипт запустил демона, тот ушёл в фон (fork + daemonize), PID записан в /var/run/x.pid. Демон упал - никто не поднимет, а PID-файл врёт.
  3. Нельзя надёжно убить сервис со всеми потомками. Демон, который форкнул десять процессов, после stop оставлял сирот.
  4. Сотни строк shell на каждый сервис, у каждого дистрибутива свои: functions в Red Hat, lsb-base в Debian.
  5. Мир стал динамическим: USB, Wi-Fi, ноутбуки, сон и пробуждение, горячее подключение дисков. Модель «загрузились по списку и всё» под это не подходила.

Кто пытался раньше

  • daemontools (Дэниел Бернстайн, 1990-е) → runit (2004) → s6 - идея супервизии: сервис работает на переднем плане, родитель-надзиратель видит его смерть и перезапускает. Минималистично и надёжно, но в мейнстрим не вышло
  • launchd от Apple (Mac OS X 10.4, 2005) - первым показал socket activation и запуск по требованию. Поттеринг позже прямо называл его источником идей
  • SMF в Solaris 10 (2005) - сервисы с зависимостями и манифестами
  • Upstart от Canonical (Скотт Джеймс Ремнант, Ubuntu 6.10, 2006) - первая серьёзная замена SysV в Linux. Событийная модель: «когда случилось событие X, запусти Y». Его взяли Ubuntu, Fedora 9-14, RHEL 6 и Chrome OS

Слабое место Upstart. Событийная модель идёт снизу вверх: администратор описывает цепочку событий, которая приведёт к запуску нужного сервиса. Естественнее сверху вниз -- «я хочу это, ему нужно вот то», а цепочку пусть выводит система. Плюс CLA от Canonical: чтобы закоммитить, нужно было передать права на код компании, и внешним разработчикам это не нравилось.


Рождение: «Rethinking PID 1» (2010)

30 апреля 2010 года Леннарт Поттеринг опубликовал пост «Rethinking PID 1» с экспериментальной системой. Делали её вдвоём с Каем Зиверсом, мейнтейнером udev. Оба работали в Red Hat. У Поттеринга к тому моменту была громкая и неоднозначная репутация автора PulseAudio и Avahi.

Идеи поста, которые определили всё дальнейшее:

  1. Запускать меньше и параллельнее. Не запускать то, что не нужно сейчас, а нужное - одновременно.
  2. Socket activation - главный трюк. Сервис B ждёт сервис A потому, что хочет подключиться к его сокету. Значит, init может сам заранее создать все сокеты (/dev/log, /run/dbus/system_bus_socket) и запустить A и B одновременно. Если B напишет в сокет раньше, чем A готов, ядро подержит данные в буфере. Порядок зависимостей перестаёт тормозить загрузку.
  3. Трекинг процессов через cgroups. Каждый сервис в своей control group ядра: init точно знает, какие процессы его, даже после десятка форков, и может убить их все. PID-файлы не нужны.
  4. Меньше shell. Вместо скриптов - декларативные unit-файлы в стиле .ini. Типовые задачи (монтирование, hostname, загрузка модулей) переписаны на C внутри init.
  5. Зависимости вместо событий. Описываешь, что нужно (Requires=, Wants=) и в каком порядке (After=), а граф система выводит сама.

Мейнтейнер Upstart Ремнант ответил на пост в тот же день. Так началось противостояние на пять лет.


Почему Red Hat

systemd не начинался как корпоративный проект «по приказу сверху». Его начали два инженера Red Hat, недовольные Upstart, практически по своей инициативе. Red Hat быстро увидел, что он решает его собственные задачи, и стал вкладываться:

  1. Быстрая и предсказуемая загрузка Fedora и RHEL - на серверах и на ноутбуках разработчиков.
  2. Надёжная супервизия для энтерпрайза. Упавший сервис поднять, при остановке убить все процессы. Для поддержки RHEL это прямые деньги: меньше тикетов «процесс висит после stop».
  3. Единый интерфейс администрирования. systemctl restart nginx и journalctl -u nginx одинаковы в любом дистрибутиве. Обучение и поддержка дешевеют, а крупнейший коммерческий игрок от стандартизации выигрывает больше всех.
  4. Контроль над критическим слоем. Upstart принадлежал Canonical и требовал CLA. Держать ключевой компонент своей ОС в руках конкурента Red Hat не хотел.
  5. cgroups и изоляция - фундамент будущих контейнеров. Red Hat рано сделал ставку на контейнеры (OpenShift, Podman), и init, который нативно управляет cgroups, лёг в эту стратегию.

Note

Миф «Red Hat захватывает Linux» в войнах за init звучал громко. Red Hat действительно получил огромное влияние на userspace Linux. Но systemd приняли технические комитеты независимых дистрибутивов (Arch, Debian, SUSE, затем Ubuntu), в основном по техническим причинам.


Хронология

Год Событие Почему важно
2010 Пост «Rethinking PID 1» Рождение проекта
2011 Fedora 15 - первый крупный дистрибутив с systemd по умолчанию. Тогда же openSUSE 12.1 Обкатка в реальном мире
2012 udev влит в репозиторий systemd Первый сигнал «systemd - не только init». Gentoo отвечает форком eudev
2012 Arch Linux переходит на systemd Самосборщики приняли - сильный сигнал
2012-2013 logind заменяет заброшенный ConsoleKit. GNOME 3.8 бросает ConsoleKit, позже части GNOME требуют logind Рабочий стол начинает зависеть от systemd: «навязывают через GNOME»
2013 Предсказуемые имена интерфейсов: enp3s0 вместо eth0 Конец гонок «какая сетевуха стала eth0 после ребута»
2014 RHEL 7 и SLES 12 выходят с systemd Энтерпрайз принял, вопрос де-факто решён
февраль 2014 Технический комитет Debian: 4 на 4 между systemd и Upstart, ничью решает председатель Бдейл Гарби в пользу systemd Главная развилка истории
2014 Через несколько дней Canonical объявляет переход Ubuntu Upstart фактически мёртв
апрель 2014 Торвальдс отказывается принимать патчи ядра от Кая Зиверса Пик конфликта ядра и systemd
ноябрь 2014 Общее голосование Debian (GR) о запрете жёстких зависимостей от одного init проваливается Решение комитета остаётся в силе
2014 Объявлен Devuan - форк Debian без systemd Раскол становится организационным
2015 Debian 8 Jessie и Ubuntu 15.04 с systemd Почти весь мейнстрим на systemd
2015 kdbus (D-Bus в ядре от команды systemd) не принимают Позже появляется userspace-замена dbus-broker
2016 KillUserProcesses=yes по умолчанию (v230): выход из сессии убивает tmux и screen Новый виток ненависти, дистрибутивы меняют дефолт
2017 Devuan 1.0 Альтернатива доведена до релиза
2019 Fedora 31 включает cgroups v2 по умолчанию - первой из крупных systemd тянет за собой контейнерную индустрию
2020 systemd-homed (v246), systemd-oomd (v247) Экспансия в домашние каталоги и управление памятью
2022 Поттеринг уходит из Red Hat в Microsoft. Основана UAPI Group - сообщество спецификаций userspace systemd становится межкорпоративным, стандарты выносятся отдельно
2024 Бэкдор в xz - путь в sshd шёл через libsystemd Урок о связанности
2024 run0 (v256) - замена sudo без SUID-бита systemd замахнулся даже на sudo
2025 v258 удаляет cgroups v1, v259 - экспериментальная поддержка musl Наследие отрезается, systemd приходит в Alpine и postmarketOS
январь 2026 Поттеринг уходит из Microsoft и с Крисом Кюлем и Кристианом Браунером основывает Amutable - компанию про верифицируемую целостность Linux Следующий фронт - доверенная загрузка
март 2026 v260 окончательно удаляет поддержку SysV init-скриптов Мост в прошлое сожжён через 16 лет. Дальше вышла v261

Войны за init

Голосование Debian, 2014. Многомесячные споры, сотни писем, отставки. Технический комитет разделился поровну, решил голос председателя. Иэн Джексон, сторонник свободы выбора init, после этого ушёл из комитета. Debian - база для Ubuntu, Mint и сотен других дистрибутивов, поэтому решение определило судьбу всей экосистемы.

Торвальдс против Зиверса, 2014. systemd начал реагировать на параметр ядра debug и заваливал логами консоль так, что система переставала нормально грузиться. Разработчики ядра попросили исправить, Зиверс ответил в духе «debug - общий параметр, не только ядра» и закрыл баг. Торвальдс публично отказался принимать от Зиверса патчи (тот как раз продвигал kdbus), пока тот не начнёт исправлять ломающее других поведение. Конфликт стал символом претензии «systemd не уважает чужие границы».

Devuan и «Veteran Unix Admins». Группа администраторов форкнула Debian под лозунгом свободы выбора init. Devuan жив до сих пор, но остался нишевым.

Бэкдор в xz, 2024 - самый поучительный эпизод.

  1. OpenSSH сам от systemd не зависит. Но Debian и Fedora патчили sshd, чтобы тот сообщал systemd о готовности через sd_notify.
  2. Для этого sshd линковался с libsystemd, а libsystemd - с liblzma (сжатие журнала).
  3. Атакующий через многолетнюю социальную инженерию стал мейнтейнером xz и встроил в liblzma бэкдор, который по этой цепочке оказывался в процессе sshd.

Последствия:

  • systemd перевёл тяжёлые зависимости libsystemd на загрузку по требованию через dlopen
  • протокол sd_notify стали рекомендовать реализовывать самим - это 10-20 строк на C без всей библиотеки
  • в самом systemd дыры не было, но эпизод наглядно показал главный довод критиков: большая связанная поверхность повышает цену любой компрометации

Влияние на индустрию

Даже противники признают: systemd изменил Linux сильнее любого userspace-проекта за последние 20 лет.

  1. Один способ администрировать любой дистрибутив. systemctl, journalctl, hostnamectl, timedatectl, localectl одинаковы в Fedora, Debian, Arch, SUSE. Unit-файл от вендора софта работает везде, а раньше каждый пакет тащил init-скрипты под каждый дистрибутив.
  2. Контейнеры и Kubernetes. systemd стал главным потребителем и двигателем cgroups v2 (единая иерархия). Когда их включила Fedora 31, Docker, runc и Kubernetes были вынуждены их поддержать. Сегодня Kubernetes рекомендует драйвер cgroup systemd, чтобы kubelet и systemd не делили одни cgroups двумя менеджерами.
  3. Стандартизация мелочей, которые раньше у всех были свои: /etc/os-release, /etc/machine-id, предсказуемые имена интерфейсов, tmpfiles.d, sysusers.d, продвижение usrmerge (/bin → /usr/bin). Позже это оформилось в спецификации UAPI Group.
  4. Супервизия как норма. Перезапуск упавшего сервиса, лимиты памяти и CPU, песочница (ProtectSystem=, PrivateTmp=, фильтры системных вызовов) - несколько строк unit-файла, а не отдельный софт.
  5. Структурированные логи. journald хранит записи с полями (_PID, _SYSTEMD_UNIT, PRIORITY), а не строки. journalctl -u nginx --since "1 hour ago" -p err стал стандартом диагностики.
  6. Доверенная загрузка и immutable-системы. systemd-boot, UKI (unified kernel image: ядро + initrd + командная строка в одном подписанном EFI-файле), measured boot с TPM2, systemd-sysext для неизменяемых образов. На этом строятся Fedora Silverblue, CoreOS, SteamOS - и ровно в эту сторону пошёл Поттеринг с Amutable.
  7. Рабочий стол. logind - стандарт сессий, мест (seats), прав на устройства и блокировок сна. GNOME, KDE и Wayland-композиторы опираются на него.

Как systemd устроен

Загрузка

UEFI/BIOS
   └─► загрузчик (GRUB или systemd-boot)
          └─► ядро + initramfs
                 └─► systemd внутри initramfs (PID 1)
                        ├─ находит и монтирует корень (cryptsetup, LVM, RAID)
                        └─► switch-root в настоящий корень
                               └─► systemd (тот же PID 1, перезапущенный из корня)
                                      ├─ генераторы: /etc/fstab → .mount-юниты и т.д.
                                      ├─ граф зависимостей от default.target
                                      ├─ сокеты, файловые системы, сервисы - параллельно
                                      └─► multi-user.target / graphical.target: система готова
systemd-analyze blame            # кто сколько грузился
systemd-analyze critical-chain   # цепочка, которая определила общее время загрузки

Юниты

Юнит - объект, которым управляет systemd. Тип - по расширению файла.

Тип Что описывает
.service Процесс или демон
.socket Сокет для socket activation: пришло соединение - запустить сервис
.target Точка синхронизации, группа юнитов. Замена runlevels: multi-user.target ≈ runlevel 3
.mount, .automount Точка монтирования, automount - при первом обращении
.swap Раздел или файл подкачки
.device Устройство, появившееся через udev
.timer Таймер, замена cron
.path Наблюдение за путём: изменился файл - запустить юнит
.slice Узел дерева cgroups для лимитов ресурсов: system.slice, user.slice
.scope Группа процессов, запущенных не systemd, но взятых под учёт: например, сессия пользователя
  • где лежат: /usr/lib/systemd/system/ - от пакетов, /etc/systemd/system/ - от администратора и приоритетнее, /run/systemd/system/ - временные
  • drop-in: foo.service.d/override.conf (systemctl edit foo) поверх оригинала, не трогая его
  • шаблоны: getty@.service разворачивается в getty@tty1.service, внутри %i = tty1

Зависимости, порядок, транзакции

systemd разделяет две независимые оси:

  • требования - Wants=, Requires=, BindsTo=: что ещё поднять
  • порядок - After=, Before=: в какой последовательности

Запуск юнита - это транзакция:

  1. systemd собирает все нужные юниты по зависимостям.
  2. Проверяет граф на циклы и конфликты (Conflicts=), выбрасывает лишнее.
  3. Превращает результат в jobs (start, stop, restart) в очереди. Jobs без отношений порядка выполняются параллельно.

Нашёлся цикл - systemd удаляет из транзакции один из необязательных юнитов и пишет об этом в журнал.


cgroups

Каждый сервис живёт в своей cgroup: /sys/fs/cgroup/system.slice/nginx.service/. Процесс не может выйти из неё сам, потомки наследуют группу.

  • systemctl stop убивает всё в группе (KillMode=control-group)
  • systemctl status показывает настоящее дерево процессов сервиса
  • лимиты MemoryMax=, CPUQuota=, TasksMax= - просто атрибуты cgroup
systemd-cgls    # дерево cgroups с процессами
systemd-cgtop   # потребление ресурсов по cgroups в реальном времени

Socket activation

  1. systemd читает foo.socket, создаёт сокет и слушает его сам.
  2. Приходит первое соединение - или сразу при загрузке, ради параллельности.
  3. systemd запускает foo.service и передаёт ему уже открытые файловые дескрипторы начиная с номера 3. Переменные LISTEN_FDS=1 и LISTEN_PID=<pid> говорят, сколько их.
  4. Сервис через sd_listen_fds() забирает сокет и работает с ним как с обычным.

Бонус: сервис можно перезапустить, не потеряв ни одного соединения. Сокет всё это время держит systemd, а входящие ждут в очереди ядра.


Готовность сервиса: sd_notify

При Type=notify systemd выставляет переменную NOTIFY_SOCKET. Сервис шлёт туда датаграмму READY=1, так же передаются STATUS=..., WATCHDOG=1 («я жив» для watchdog), RELOADING=1. Протокол тривиальный, и вся libsystemd для него не нужна - что и выяснилось на истории с xz.


Как с systemd разговаривают

  • D-Bus - основной API менеджера: systemctl вызывает методы объекта org.freedesktop.systemd1. Пока шина не поднялась (ранняя загрузка) - приватный сокет /run/systemd/private
  • Varlink - более новый и простой IPC: JSON поверх Unix-сокетов. Компоненты всё активнее переходят на него (userdb, homed, resolved): D-Bus тяжёлый и сам зависит от уже работающей системы

Генераторы

Генератор - маленькая программа, которую systemd запускает очень рано, до построения графа. Она превращает чужие конфиги в юниты: systemd-fstab-generator делает из каждой строки /etc/fstab .mount-юнит. Так systemd держал совместимость: SysV-скрипты много лет превращал в юниты systemd-sysv-generator, пока v260 его не удалила.


Состав проекта

Компонент Что делает Что заменил
systemd (PID 1) Менеджер системы и сервисов sysvinit, Upstart
systemd --user Такой же менеджер для сервисов пользователя -
journald Журнал: структурированные записи с метаданными, бинарные файлы, ротация, опционально пересылка в syslog Частично rsyslog и syslog-ng
udevd Устройства: правила, имена, права, события hotplug Влит в проект в 2012
logind Сессии пользователей, seats, права на устройства, блокировки сна и выключения ConsoleKit
networkd Настройка сети на серверах и в контейнерах ifupdown, network-scripts. На десктопе обычно NetworkManager
resolved Кэширующий DNS-резолвер: DNSSEC, DNS-over-TLS, сплит-DNS для VPN dnsmasq, nscd
timesyncd Простой SNTP-клиент ntpd. Для серверов точнее chrony
homed Переносимые, при желании зашифрованные домашние каталоги с метаданными пользователя Классические /home + /etc/passwd
oomd OOM-killer в userspace на метриках давления памяти (PSI) Поздний OOM-killer ядра
machined, nspawn Регистрация ВМ и контейнеров, лёгкие контейнеры chroot «на стероидах»
tmpfiles, sysusers Временные файлы и каталоги, системные пользователи из декларативных файлов Логика в postinstall-скриптах пакетов
systemd-boot, ukify Простой UEFI-загрузчик, сборка UKI GRUB на UEFI-системах
cryptsetup, repart, sysext Шифрование дисков, разметка при загрузке, расширения неизменяемого образа Самописные скрипты в initramfs
run0 Запуск команды от другого пользователя без SUID-бинарника sudo
coredump Сбор и хранение дампов упавших процессов core в текущем каталоге
hostnamectl, timedatectl, localectl, loginctl, networkctl, resolvectl Единые утилиты администрирования Разные конфиги в разных дистрибутивах

Note

Это один репозиторий и одна команда, но не монолитный бинарник, а десятки отдельных программ. Многие компоненты опциональны и выключаются при сборке: можно жить с systemd в роли init без networkd, resolved и homed. Так и делают многие дистрибутивы.


Альтернативы

Система Модель Где живёт
sysvinit Классика: скрипты и runlevels Devuan (по выбору), Slackware, MX Linux
OpenRC Скрипты с зависимостями поверх любого init, опциональная супервизия Gentoo, Alpine, Artix
runit Минималистичная супервизия в духе daemontools Void Linux, Artix
s6 / s6-rc Супервизия + менеджер зависимостей, очень строгий дизайн (Laurent Bercot) Artix, Obarun, контейнеры (s6-overlay)
dinit Современный компактный менеджер сервисов с зависимостями на C++ Chimera Linux, Artix
GNU Shepherd Сервисы описываются на Scheme (Guile) GNU Guix
launchd Источник идеи socket activation macOS
SMF Сервисы с манифестами и зависимостями Solaris, illumos
rc.d Скрипты BSD FreeBSD, OpenBSD, NetBSD
Upstart Событийная модель Почти вымер

Прослойки, чтобы жить без systemd:

  • eudev - udev без systemd
  • elogind - logind без systemd, чтобы работали GNOME и KDE
  • ConsoleKit2 - форк ConsoleKit

См. Дистрибутивы Linux.


За и против

За:

  • быстрая параллельная загрузка
  • надёжная супервизия
  • cgroups и песочница из коробки
  • один интерфейс на все дистрибутивы
  • структурированные логи
  • активная разработка и поддержка крупнейших вендоров

Против:

  • нарушение «философии UNIX» (одна программа - одна задача): проект поглотил десятки областей
  • связанность: части десктопа и софта начинают требовать компоненты systemd, и выбор init перестаёт быть свободным
  • бинарный журнал: без journalctl не прочитать grep-ом, повреждённый файл спасать сложнее
  • большая поверхность атаки: PID 1 и окружение - сотни тысяч строк на C. xz показал цену связанности
  • стиль команды: «работает как задумано» в ответ на баг-репорты, конфликты с ядром, Debian и пользователями

Взвешенно. Технически systemd решил реальные проблемы, которые SysV не решал 20 лет. Поэтому его и приняли технические комитеты почти всех мейнстримных дистрибутивов. Претензии к масштабу и связанности тоже обоснованы. Поэтому живут альтернативы, а сам systemd постепенно делает части опциональными: dlopen вместо жёстких зависимостей, поддержка musl.


Итого:

  • systemd - PID 1 + целый слой системной обвязки Linux: сервисы, журнал, устройства, сессии, сеть, загрузка
  • создан в 2010 году Поттерингом и Зиверсом в Red Hat. Red Hat поддержал, потому что проект решал задачи энтерпрайза: быстрая загрузка, супервизия, единое администрирование, cgroups под контейнеры
  • ключевые идеи: socket activation, трекинг через cgroups, декларативные юниты, зависимости вместо событий, транзакции
  • перелом - 2014 год: Debian, RHEL 7, SLES 12, капитуляция Upstart. К 2015 году мейнстрим на systemd
  • влияние: единое администрирование, cgroups v2 и драйвер cgroup в Kubernetes, стандартизация (os-release, machine-id, имена интерфейсов), доверенная загрузка и immutable-ОС
  • 2025-2026: удалены cgroups v1 и поддержка SysV-скриптов (v260), экспериментальная поддержка musl, создатель ушёл строить «верифицируемый Linux» в Amutable

Источники