Заметки об ОС Linux. Часть 3. Система инициализации

Как было сказано ранее, /sbin/init — это программа, которая стартует первой в системе (в пользовательском режиме). Обычно имеет PID=1. Главная задача системы инициализации (далее — СИ) — довести систему до состояния, пригодного для использования. Для этого СИ может монтировать какие-либо разделы и файловые системы (так например на определённом этапе загрузки СИ выполняет монтирование ряда ФС), а также запускать установленные в системе демоны и иные программы. Основные параметры загрузки содержатся в файле /etc/inittab.

В некоторых СИ используются уровни выполнения — определённые стадии загрузки системы, на которых пользователю доступны какие-либо её возможности.

Помимо загрузки ОС, СИ заботится и о её выключении, завершая все сервисы и размонтируя файловые системы, после чего вызывая системный вызов для отключения ПК.

Реализации init'ов

Простейшие СИ

По умолчанию ядро после своей инициализации запускает программу /sbin/init и паникует, если не нашло её. Однако данное поведение можно и изменить, передав параметр init=/путь/до/СИ. Например, в качестве инита вы можете использовать вашу командную оболочку:

init=/bin/ash

Тогда первым процессом будет запущена оболочка ash из состава BusyBox. Однако помните, что в задачи системы инициализации входит подготовка ОС к дальнейшему функционированию: монтирование различных файловых систем (в т.ч. всяких /dev/, /proc/, /sys/), запуск демонов и иных программ. Если вместо обычной системы инициализации или программы, её заменяющей, вы запустите командную оболочку, то ничего из этого выполнено не будет — у вас просто запустится shell. Вы получите как бы рабочую систему с правами root, но не более того. Не будут работать логирование, графика, сеть и куча других вещей, которые запускает СИ.

Идя по пути усложнения вещей, мы можем написать shell-скрипт, который выполняет какие-то самые базовые вещи по настройке и запуску ОС. Например:

#!/bin/ash

export PATH=/bin:/sbin

# Монтируем всякие разные файловые системы
mount proc /proc -t proc
mkdir -p /dev/pts
mount -t devpts -o noexec,nosuid,gid=5,mode=0620 devpts /dev/pts
mount -t tmpfs none -o nodev,nosuid,noatime /tmp
mount -t tmpfs none -o nodev,nosuid,noatime /var/log
mount -t tmpfs none -o nodev,nosuid,noatime /var/run
mount -t tmpfs none -o nodev,nosuid,noatime /run
mount sysfs /sys -t sysfs

# Запускаем системный логгер
syslog
klogd -c 3

# Инициализация одного TTY
getty tty1 # Управление передаётся процессу `/sbin/getty`

# Когда мы завершим процесс `getty`, можно считать,
# что мы выключаем систему. Поэтому нам нужны
# действия по завершению её работы:

# Завершаем запущенные в скрипте сервисы
killall klogd
killall syslog

# Убиваем всё остальное
kill -TERM -1
kill -KILL -1

# Размонтируем все ФС
umount -a

exit

SysVInit

Ранее SysVInit был де-факто стандартом в дистрибутивах Linux, но в 2010-х были относительно успешные попытки заменить его на другие СИ.

Достоинства:

  • Устоявшаяся и хорошо понятная классическая система;
  • Простой процесс настройки;
  • Нетребовательность к ресурсам компьютера;
  • Следование философии UNIX;

Недостатки:

  • Неудобная работа с сервисами;
  • Каждый сервис — это обычный shell-скрипт;
  • Отсутствие параллельного запуска загрузочных скриптов, из-за чего хотя бы один «зависший» скрипт застопорит загрузку/выключение системы;

Для SysVInit требуются загрузочные скрипты. С примером таких скриптов вы можете ознакомиться в руководстве LFS.

OpenRC

OpenRC — система инициализации с поддержкой зависимостей. Запускает необходимые системные сервисы в определённом порядке при загрузке, управляет ими во время работы системы и останавливает их при завершении работы. Имеет возможность параллельного запуска процессов для сокращения времени загрузки.

runit

runit — кроссплатформенная система инициализации для систем семейства UNIX, позиционирующая себя как замена SysVInit и других СИ. Как и другие системы инициализации, запускается первым процессом в системе. Работа runit состоит из трёх этапов:

  1. runit запускает /etc/runit/1 и ждёт, пока он завершится.
  2. Запускает /etc/runit/2, который не должен завершаться до тех пор, пока система не остановится или не перезагрузится. В случае сбоя /etc/runit/2 перезапускается. На этом этапе он также опционально может обрабатывать сигнал INT (нажатие Ctrl+Alt+Del на клавиатуре).
  3. Если runit было сказано остановить или перезагрузить систему, либо если этап №2 завершился без ошибок, то запускается /etc/runit/3, который выполняет задачи по выключению, остановке или перезагрузке.

runit предоставляет следующие возможности:

  • демонизация процессов;
  • журналирование вывода процессов;
  • запуск, остановка, перезапуск сервисов;
  • выключение или запуск сервисов автоматически по мере их появления или удаления из списка;
  • и т.д.

systemd

systemd — более крупная и сложная программа, чем все остальные, которые здесь описаны. Представляет собой менеджер системы и служб, который выполняется как самый первый процесс в системе и запускает остальные её компоненты. Параллельно запускает сервисы, а также имеет поддержку зависимостей у этих сервисов.

Поддерживает сценарии SysVInit, да собственно и является заменой последнего.

Среди прочих его функций можно отметить программу journalctl для журналирования системы, утилиты управления базовой конфигурацией системы (установка имени хоста, настройка даты и времени, etc.), ведение списка вошедших в систему пользователей, системных учётных записей, запущенных контейнеров, виртуальных машин, т.д.

systemd использует юниты — конфигурационные файлы, синтаксис которых напоминает формат *.ini, которые содержат в себе команды для монтирования файловых систем, запуска системных компонентов, включения подкачки и т.д.:

  • сервисы (*.service);
  • точки монтирования файловых систем (*.mount);
  • устройства (*.device);
  • сокеты (*.socket);
  • etc.

Некоторым людям не особо нравится systemd за его монолитность (хотя в системах Linux куча других монолитных компонентов, то же ядро например) и то, что он пытается заменить собой кучу других компонентов системы, а не только /sbin/init.

Сравнение систем инициализации

Таблица взята из Gentoo WiKi.

ХарактеристикаSysVInitOpenRCrunitsystemd
Поддерживаемые платформыLinux/BSDLinux/BSDLinux/BSD/DarwinLinux
Основной язык программированияCPOSIX Shell + CCC
Основные зависимостиinit (SysVInit или BSD)D-Bus
Формат скрипта/сервисаОдин скрипт (который может запускать несколько других)Shell-скриптыShell-скриптыКонфиг ini
Конфигурация для каждого сервисаНетЕстьНетЕсть
Параллельная загрузка сервисовНетЕсть (опционально)Есть (???)Есть