первый процесс, который запускает ядро — и самый идеологически спорный вопрос всей экосистемы Linuxthe first process the kernel starts — and the most ideologically contested question in the whole Linux ecosystem

Init-система: PID 1 и то, что вокруг негоThe init system: PID 1 and what surrounds it

После того как загрузчик передал управление ядру, а ядро распаковало initramfs и смонтировало корневую файловую систему, ядру нужен ровно один процесс с PID 1 — тот, кого оно запускает первым и кому в конце концов «усыновляет» осиротевшие процессы. Всё остальное — как этот процесс решает, что и в каком порядке запускать дальше — не диктуется ядром вообще, и именно поэтому разные init-системы (systemd, OpenRC, runit, SysVinit) решают одну и ту же задачу совершенно разными архитектурами: от полноценного графа зависимостей и параллельного запуска до нарочито минималистичного дерева процессов-супервизоров без единой строчки конфигурации сверх необходимого.Once the bootloader has handed control to the kernel, and the kernel has unpacked the initramfs and mounted the root filesystem, the kernel needs exactly one process with PID 1 — the one it starts first and to whom it eventually "adopts" orphaned processes. Everything else — how that process decides what to start next and in what order — isn't dictated by the kernel at all, which is exactly why different init systems (systemd, OpenRC, runit, SysVinit) solve the same problem with completely different architectures: from a full dependency graph with parallel startup to a deliberately minimalist tree of supervisor processes with not one line of configuration beyond what's necessary.

Что этоWhat it is

PID 1 — не просто «первый процесс по номеру», а процесс с особым статусом в самом ядре: ему нельзя послать SIGKILL так, как обычному процессу (ядро это блокирует), и именно он становится новым родителем для любого процесса, чей родной родитель завершился раньше него (reparenting) — без этого механизма такие процессы зависали бы в системе как неубираемые «зомби». Init-система — это программа, которая занимает PID 1 и берёт на себя обе роли: запуск и остановку всех остальных сервисов системы в правильном порядке, и «уборка» осиротевших процессов на протяжении всей работы системы.PID 1 isn't just "the first process by number" — it's a process with special status inside the kernel itself: it can't be sent SIGKILL the way an ordinary process can (the kernel blocks that), and it's the one that becomes the new parent for any process whose original parent exited before it did (reparenting) — without this mechanism, such processes would hang around the system as un-collectable "zombies." An init system is the program that occupies PID 1 and takes on both roles: starting and stopping every other system service in the right order, and cleaning up orphaned processes for as long as the system runs.

До 2010 года подавляющее большинство дистрибутивов Linux использовали SysVinit — прямого потомка init из UNIX System V (1983 год): последовательность shell-скриптов, организованных по «уровням выполнения» (runlevel), запускаемых строго один за другим. Простая и понятная модель, но с двумя фундаментальными ограничениями — запуск строго последовательный (даже независимые друг от друга сервисы ждут своей очереди) и никакого встроенного понятия «эта служба реально готова к работе», только «скрипт запуска завершился». В 2010 году инженер Red Hat Леннарт Поттеринг выпустил systemd — замену init, вводящую параллельный запуск на основе графа зависимостей и глубокую интеграцию с возможностями ядра (cgroups, sockets activation). К Fedora 2011 года присоединились openSUSE и Arch Linux в 2012-м, и к 2015 году systemd стал стандартом по умолчанию почти во всех крупных дистрибутивах — Debian, Ubuntu, Fedora, RHEL, SUSE, Arch. Переход сопровождался одним из самых ожесточённых идеологических споров в истории Linux (подробнее — в разделе «Грабли и мифы» ниже), но по состоянию на 2026 год спор фактически решён рыночной долей: systemd используется по умолчанию примерно в 85% десктопных и серверных дистрибутивов, а альтернативы — OpenRC, runit, s6, dinit, SysVinit — сохраняются в основном в дистрибутивах, для которых архитектурная позиция против systemd — часть идентичности (Gentoo, Alpine, Void, Devuan, Artix, Chimera).Before 2010, the overwhelming majority of Linux distros used SysVinit — a direct descendant of UNIX System V's init (1983): a sequence of shell scripts organized into "runlevels," run strictly one after another. A simple and understandable model, but with two fundamental limits — startup was strictly sequential (even services independent of each other waited their turn), and there was no built-in notion of "this service is actually ready to work," only "the startup script finished." In 2010, Red Hat engineer Lennart Poettering released systemd — a replacement for init introducing dependency-graph-based parallel startup and deep integration with kernel capabilities (cgroups, socket activation). Fedora adopted it in 2011, joined by openSUSE and Arch Linux in 2012, and by 2015 systemd had become the default standard across nearly every major distro — Debian, Ubuntu, Fedora, RHEL, SUSE, Arch. The transition came with one of the most heated ideological debates in Linux history (more on this below, in Gotchas & myths), but as of 2026 the debate is effectively settled by market share: systemd is the default on roughly 85% of desktop and server distros, while the alternatives — OpenRC, runit, s6, dinit, SysVinit — mostly survive in distros for which an architectural stance against systemd is part of their identity (Gentoo, Alpine, Void, Devuan, Artix, Chimera).

Как это устроено внутриHow it's built internally

Init-системы для Linux делятся на два принципиально разных архитектурных семейства, а не просто «варианты одной идеи» — они по-разному отвечают на вопрос «что вообще нужно системе от PID 1»:Linux init systems split into two fundamentally different architectural families, not just "variations on one idea" — they answer differently the question of what a system actually needs from PID 1:

семействоfamilyпредставителиrepresentativesцентральная идеяcentral idea
граф зависимостейdependency graphsystemd, OpenRCявно описанные зависимости и порядок между сервисами (Requires/After и подобное); система сама строит граф и запускает независимые ветки параллельноexplicitly declared dependencies and ordering between services (Requires/After and the like); the system builds the graph itself and starts independent branches in parallel
supervision suitesupervision suiterunit, s6, dinitкрошечный PID 1 плюс дерево маленьких процессов-супервизоров, по одному на сервис; нет графа зависимостей как такового — обычно просто алфавитный порядок каталогов и постоянное присмотр за тем, жив ли процессa tiny PID 1 plus a tree of small supervisor processes, one per service; no dependency graph as such — usually just alphabetical directory order and constant watching over whether the process is alive

В systemd единица конфигурации — unit-файл: простой текстовый файл, описывающий не только сервисы (.service), но и точки монтирования, сокеты, таймеры и другие управляемые сущности единым форматом. Ключевая деталь, которую часто путают: зависимость (Requires=, Wants=) и порядок запуска (After=, Before=) — это два независимых друг от друга объявления, а не одно и то же:In systemd, the unit of configuration is the unit file: a plain text file describing not just services (.service) but mount points, sockets, timers, and other managed entities, all in one format. A detail often confused: a dependency (Requires=, Wants=) and a start order (After=, Before=) are two independent declarations, not the same thing:

unit A.service запущен параллельно с другими Requires=A + After=A unit B.service ждёт активного состояния A unit C.service Requires=D, без After unit D.service активируется вместе с C — порядок не гарантирован unit E.service не связан ни с кем — стартует параллельно всем Requires= — только требование активации вместе, без гарантии порядка After= — только порядок («стартовать после того как A станет active или failed»), без требования активации оба вместе (как у B относительно A) — то, что интуитивно ожидают от «зависимости»: обязательно и по порядку
Requires= без After= гарантирует лишь совместную активацию, но не порядок — на практике оба юнита могут стартовать одновременно. Только явная комбинация обеих директив даёт интуитивно ожидаемое поведение «сначала одно, потом другое». Юниты без объявленных связей друг с другом запускаются параллельно — именно за счёт этого systemd существенно сокращает время загрузки по сравнению с последовательным SysVinit.Requires= without After= only guarantees joint activation, not ordering — in practice both units can start simultaneously. Only the explicit combination of both directives gives the intuitively expected "one then the other" behavior. Units with no declared relationship to each other start in parallel — this is exactly what lets systemd substantially cut boot time compared to sequential SysVinit.

Юниты группируются в target — аналог runlevel из SysVinit, но не взаимоисключающий: несколько target могут быть активны одновременно, и один target может требовать активации другого. Помимо запуска сервисов по требованию (обычный .service), systemd умеет откладывать реальный старт демона до первого обращения к его сокету (socket activation) — сокет создаётся сразу, а сам процесс демона запускается только при первом подключении к нему, что и ускоряет параллельный старт, и экономит ресурсы на сервисах, которые в конкретной загрузке могут вообще не понадобиться.Units are grouped into targets — a SysVinit runlevel analogue, but not mutually exclusive: several targets can be active at once, and one target can require another to activate. Besides starting services on demand (an ordinary .service), systemd can defer a daemon's actual startup until the first request hits its socket (socket activation) — the socket is created immediately, but the daemon process itself only starts on the first connection to it, which both speeds up parallel startup and saves resources on services that a particular boot might not need at all.

Другая ветка — supervision suites вроде runit — устроена нарочито проще. Единица конфигурации там не декларативный файл, а исполняемый скрипт: сервис runit — это каталог, содержащий единственный исполняемый файл с именем run, который супервизор запускает и держит запущенным, перезапуская при завершении. Порядок между сервисами обычно не описывается графом вообще, а определяется алфавитным порядком каталогов — руководство проекта прямо декларирует минимализм кода как принцип безопасности: исходный код runit.c в версии 1.0.0 занимает 330 строк, runsvdir.c — 274, runsv.c — 509, что делает возможным построчный аудит всей системы одним человеком за разумное время.The other branch — supervision suites like runit — is deliberately simpler. Its unit of configuration isn't a declarative file but an executable script: a runit service is a directory containing a single executable file named run, which the supervisor starts and keeps running, restarting it on exit. Ordering between services usually isn't described by a graph at all — it's just alphabetical directory order — and the project explicitly states code minimalism as a security principle: the runit.c source in version 1.0.0 is 330 lines, runsvdir.c is 274, runsv.c is 509, making a line-by-line audit of the whole system feasible for one person in a reasonable amount of time.

Ещё одна архитектурная особенность systemd, отсутствующая у большинства альтернатив, — то, что каждый запущенный сервис помещается в собственную cgroup (control group ядра). Из этого следует не только точный учёт ресурсов на сервис (тот же механизм, на котором строится ограничение ресурсов контейнеров), но и надёжная остановка: убить все дочерние процессы демона, включая те, что он мог форкнуть и «потерять» из вида, можно одним действием над всей cgroup, а не попыткой отследить дерево процессов вручную через PID.Another systemd architectural feature most alternatives lack is that every running service is placed in its own cgroup (kernel control group). This gives not just precise per-service resource accounting (the same mechanism container resource limits are built on), but also reliable shutdown: killing all of a daemon's child processes, including ones it may have forked and "lost track of," can be done in one action against the whole cgroup, rather than trying to track the process tree manually via PID.

Как этим пользуются на практикеHow it's used in practice

командаcommandчто происходитwhat happens
systemctl status nginxпоказывает текущее состояние юнита, его cgroup со всеми дочерними процессами и последние строки журнала — типичная отправная точка при диагностике «сервис не работает»shows the unit's current state, its cgroup with all child processes, and the latest journal lines — the typical starting point when diagnosing "the service isn't working"
systemd-analyze blameвыводит список юнитов, отсортированный по времени, которое каждый из них занял при загрузке — прямой инструмент для поиска, что именно замедляет старт системыprints a list of units sorted by how long each one took during boot — a direct tool for finding exactly what's slowing the system's startup
systemctl list-dependencies nginxстроит дерево зависимостей конкретного юнита — практический способ увидеть тот самый граф Requires/After, а не держать его в головеbuilds a specific unit's dependency tree — a practical way to actually see that Requires/After graph instead of keeping it in your head
journalctl -u nginx -fпоказывает журнал конкретного юнита в реальном времени — центральное место логов при systemd вместо разрозненных файлов в /var/log для каждого демона по отдельностиshows a specific unit's journal in real time — the central place for logs under systemd, instead of scattered files in /var/log for each daemon separately
rc-service nginx start / rc-update add nginx defaultзапуск сервиса и добавление его в автозапуск в OpenRC — та же задача, что systemctl start/enable в systemd, но без единого объединяющего инструмента для обеих операцийstarting a service and adding it to autostart under OpenRC — the same task as systemctl start/enable in systemd, but with no single unified tool for both operations
sv status nginx / sv restart nginxrunit: проверка состояния и перезапуск сервиса через sv — прямое обращение к супервизору конкретного сервиса, без промежуточного слоя graph resolver-аrunit: checking a service's state and restarting it via sv — a direct call to that specific service's supervisor, with no intermediate graph-resolver layer

Грабли и мифыGotchas & myths

спор про systemd — не просто вкусовщина, а спор о unix-философииthe systemd debate isn't just a matter of taste — it's a debate about unix philosophy

Ключевая претензия критиков не «мне не нравится синтаксис», а более фундаментальная: systemd бросает вызов принципу «одна программа — одна задача», беря на себя не только запуск сервисов, но и логирование (journald), управление сетью (systemd-networkd), NTP, DNS-резолвинг и многое другое в рамках одного проекта. Один из практических аргументов — journald хранит журнал в бинарном формате, который нельзя просто прогрепать текстовым grep напрямую, нужен journalctl; при повреждении базы журнала (что случается) есть риск потерять весь журнал целиком без текстового fallback. Это не вопрос «кто прав», а реальный архитектурный компромисс: единая экосистема с общими соглашениями против классической unix-модели маленьких независимых утилит.the critics' core complaint isn't "I don't like the syntax" — it's more fundamental: systemd challenges the "one program, one job" principle by taking on not just service startup but logging (journald), network management (systemd-networkd), NTP, DNS resolution, and more within one project. One practical argument: journald stores its log in a binary format that can't be grepped directly with plain text grep — journalctl is required; if the journal database gets corrupted (and it does), there's a risk of losing the entire log with no text fallback. This isn't a question of "who's right" — it's a real architectural trade-off: one unified ecosystem with shared conventions versus the classic unix model of small independent utilities.

«systemd победил» означает рыночную долю, а не техническое единогласие"systemd won" means market share, not technical unanimity

По состоянию на 2026 год реально используются в проде минимум шесть разных init-систем: systemd на подавляющем большинстве десктопных и серверных дистрибутивов, OpenRC (Gentoo, Alpine, Artix), runit (Void Linux, Artix), s6 (Adélie, Obarun), dinit (Chimera Linux, Artix), SysVinit (Devuan, antiX). Утверждение «все перешли на systemd» неточно — точнее сказать, что каждый крупный мейнстрим-дистрибутив выбрал systemd, а часть сообщества сознательно осталась на альтернативах именно из-за архитектурных разногласий, а не по инерции.as of 2026, at least six different init systems see real production use: systemd on the overwhelming majority of desktop and server distros, OpenRC (Gentoo, Alpine, Artix), runit (Void Linux, Artix), s6 (Adélie, Obarun), dinit (Chimera Linux, Artix), SysVinit (Devuan, antiX). The claim "everyone switched to systemd" is imprecise — it's more accurate to say every major mainstream distro chose systemd, while part of the community deliberately stayed on alternatives specifically over architectural disagreements, not out of inertia.

Requires= без After= — источник неочевидных гонок при стартеRequires= without After= — a source of non-obvious startup races

Частая ошибка при написании собственных unit-файлов — предположить, что Requires= сам по себе задаёт и порядок. На практике сервис, зависящий от базы данных только через Requires= без After=, может успешно стартовать раньше, чем база данных реально готова принимать соединения — и упасть с ошибкой подключения, которая выглядит как «зависимость не сработала», хотя формально она сработала (оба юнита были активированы вместе).a common mistake when writing custom unit files is assuming Requires= alone also sets the order. In practice, a service depending on a database only via Requires= without After= can successfully start before the database is actually ready to accept connections — and crash with a connection error that looks like "the dependency didn't work," even though formally it did (both units were activated together).

ТерминыTerms

PID 1
процесс с особым статусом в ядре — не может быть завершён обычным SIGKILL и становится новым родителем для осиротевших процессов (reparenting); init-система — программа, занимающая этот PID.a process with special kernel status — can't be killed with a plain SIGKILL and becomes the new parent for orphaned processes (reparenting); an init system is the program occupying this PID.
unit / unit file (systemd)
единица конфигурации systemd — текстовый файл, описывающий сервис, точку монтирования, сокет, таймер и другие управляемые сущности единым форматом.systemd's configuration unit — a text file describing a service, mount point, socket, timer, or other managed entity, all in one format.
target (systemd)
группа юнитов, запускаемых/останавливаемых согласованно — аналог runlevel из SysVinit, но не взаимоисключающий: несколько target могут быть активны одновременно.a group of units started/stopped in a coordinated fashion — a SysVinit runlevel analogue, but not mutually exclusive: several targets can be active at once.
socket activation
отложенный запуск демона до первого обращения к его сокету — сокет создаётся сразу при загрузке, реальный процесс стартует только по первому подключению.deferring a daemon's startup until the first request to its socket — the socket is created immediately at boot, the actual process starts only on the first connection.
supervision suite
архитектурная модель init-системы (runit, s6, dinit) без явного графа зависимостей — крошечный PID 1 плюс дерево маленьких процессов-супервизоров, по одному на сервис, постоянно следящих за тем, жив ли процесс.an init system architecture (runit, s6, dinit) with no explicit dependency graph — a tiny PID 1 plus a tree of small supervisor processes, one per service, continuously watching whether the process is alive.
runlevel (SysVinit)
взаимоисключающее состояние системы в SysVinit (например, однопользовательский режим, многопользовательский без сети, полный многопользовательский) — предшественник target из systemd, но без возможности одновременной активности нескольких состояний.a mutually exclusive system state in SysVinit (e.g. single-user mode, multi-user without networking, full multi-user) — the predecessor of systemd's target, but without several states being able to be active at once.
cgroup (в контексте init)(in the init context)
control group ядра, в которую systemd помещает каждый запущенный сервис — даёт точный учёт ресурсов и надёжную остановку всех дочерних процессов сервиса одним действием, без ручного отслеживания дерева PID.a kernel control group that systemd places every running service into — gives precise resource accounting and reliable shutdown of all a service's child processes in one action, with no manual PID-tree tracking.