При загрузке компьютера множество сообщений, пробегающих на консоли, отображает выполнение многочисленных автоматических начальных инициализаций и настроек. Иногда может возникнуть желание несколько изменить работу этого этапа, а значит, необходимо хорошо её понимать. Помочь в этом — назначение данного раздела.
Сначала BIOS получает контроль над компьютером, определяет диски, считывает главную загрузочную запись и запускает загрузчик. Загрузчик принимает управление, находит ядро на диске, считывает и запускает его. Затем ядро инициализируется и начинает поиск и монтирование корневой файловой системы и, наконец, запускает первую программу — init
. Зачастую эти «корневой раздел» и init
на самом деле находятся на виртуальной файловой системе, существующей только в ОЗУ (отсюда её название — initramfs, ранее — initrd, от initialization RAM disk). Эта файловая система загружается в память загрузчиком, часто из файла на жёстком диске или по сети. Он содержит самый минимум, необходимый для того, чтобы ядро загрузило «настоящую» корневую файловую систему: сюда могут входить модуля ядра для жёсткого диска или других устройств, без которых система не способна загрузиться, или, чаще, сценарии инициализации и модули для сборки массивов RAID, открытия зашифрованных разделов, активации томов LVM и т. п. Когда корневой раздел примонтирован, initramfs передаёт управление настоящему init
, и система возвращается к стандартному процессу загрузки.
9.1.1. Система инициализации systemd
«Настоящий init» сейчас предоставляется systemd, и в данном разделе описывается эта система инициализации.
Systemd executes several processes, in charge of setting up the system: keyboard, drivers, filesystems, network, services. It does this while keeping a global view of the system as a whole, and the requirements of the components. Each component is described by a “unit file” (sometimes more); the general syntax is derived from the widely-used “*.ini files“ syntax, with key = value
pairs grouped between [section]
headers. Unit files are stored under /lib/systemd/system/
and /etc/systemd/system/
; they come in several flavours, but we will focus on “services” and “targets” here.
A systemd “service file” describes a process managed by systemd. It contains roughly the same information as old-style init-scripts, but expressed in a declaratory (and much more concise) way. Systemd handles the bulk of the repetitive tasks (starting and stopping the process, checking its status, logging, dropping privileges, and so on), and the service file only needs to fill in the specifics of the process. For instance, here is the service file for SSH:
[Unit]
Description=OpenBSD Secure Shell server
After=network.target auditd.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
[Service]
EnvironmentFile=-/etc/default/ssh
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
[Install]
WantedBy=multi-user.target
Alias=sshd.service
As you can see, there is very little code in there, only declarations. Systemd takes care of displaying progress reports, keeping track of the processes, and even restarting them when needed.
A systemd “target file” describes a state of the system, where a set of services are known to be operational. It can be thought of as an equivalent of the old-style runlevel. One of the targets is local-fs.target
; when it is reached, the rest of the system can assume that all local filesystems are mounted and accessible. Other targets include network-online.target
and sound.target
. The dependencies of a target can be listed either within the target file (in the Requires=
line), or using a symbolic link to a service file in the /lib/systemd/system/targetname.target.wants/
directory. For instance, /etc/systemd/system/printer.target.wants/
contains a link to /lib/systemd/system/cups.service
; systemd will therefore ensure CUPS is running in order to reach printer.target
.
Since unit files are declarative rather than scripts or programs, they cannot be run directly, and they are only interpreted by systemd; several utilities therefore allow the administrator to interact with systemd and control the state of the system and of each component.
The first such utility is systemctl
. When run without any arguments, it lists all the unit files known to systemd (except those that have been disabled), as well as their status. systemctl status
gives a better view of the services, as well as the related processes. If given the name of a service (as in systemctl status ntp.service
), it returns even more details, as well as the last few log lines related to the service (more on that later).
Starting a service by hand is a simple matter of running systemctl start servicename.service
. As one can guess, stopping the service is done with systemctl stop servicename.service
; other subcommands include reload
and restart
.
To control whether a service is active (i.e. whether it will get started automatically on boot), use systemctl enable servicename.service
(or disable
). is-enabled
allows checking the status of the service.
An interesting feature of systemd is that it includes a logging component named journald
. It comes as a complement to more traditional logging systems such as syslogd
, but it adds interesting features such as a formal link between a service and the messages it generates, and the ability to capture error messages generated by its initialisation sequence. The messages can be displayed later on, with a little help from the journalctl
command. Without any arguments, it simply spews all log messages that occurred since system boot; it will rarely be used in such a manner. Most of the time, it will be used with a service identifier:
#
journalctl -u ssh.service
-- Logs begin at Tue 2015-03-31 10:08:49 CEST, end at Tue 2015-03-31 17:06:02 CEST. --
Mar 31 10:08:55 mirtuel sshd[430]: Server listening on 0.0.0.0 port 22.
Mar 31 10:08:55 mirtuel sshd[430]: Server listening on :: port 22.
Mar 31 10:09:00 mirtuel sshd[430]: Received SIGHUP; restarting.
Mar 31 10:09:00 mirtuel sshd[430]: Server listening on 0.0.0.0 port 22.
Mar 31 10:09:00 mirtuel sshd[430]: Server listening on :: port 22.
Mar 31 10:09:32 mirtuel sshd[1151]: Accepted password for roland from 192.168.1.129 port 53394 ssh2
Mar 31 10:09:32 mirtuel sshd[1151]: pam_unix(sshd:session): session opened for user roland by (uid=0)
Another useful command-line flag is -f
, which instructs journalctl
to keep displaying new messages as they are emitted (much in the manner of tail -f file
).
If a service doesn't seem to be working as expected, the first step to solve the problem is to check that the service is actually running with systemctl status
; if it is not, and the messages given by the first command are not enough to diagnose the problem, check the logs gathered by journald about that service. For instance, assume the SSH server doesn't work:
#
systemctl status ssh.service
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/lib/systemd/system/ssh.service; enabled)
Active: failed (Result: start-limit) since Tue 2015-03-31 17:30:36 CEST; 1s ago
Process: 1023 ExecReload=/bin/kill -HUP $MAINPID (code=exited, status=0/SUCCESS)
Process: 1188 ExecStart=/usr/sbin/sshd -D $SSHD_OPTS (code=exited, status=255)
Main PID: 1188 (code=exited, status=255)
Mar 31 17:30:36 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:36 mirtuel systemd[1]: ssh.service start request repeated too quickly, refusing to start.
Mar 31 17:30:36 mirtuel systemd[1]: Failed to start OpenBSD Secure Shell server.
Mar 31 17:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
#
journalctl -u ssh.service
-- Logs begin at Tue 2015-03-31 17:29:27 CEST, end at Tue 2015-03-31 17:30:36 CEST. --
Mar 31 17:29:27 mirtuel sshd[424]: Server listening on 0.0.0.0 port 22.
Mar 31 17:29:27 mirtuel sshd[424]: Server listening on :: port 22.
Mar 31 17:29:29 mirtuel sshd[424]: Received SIGHUP; restarting.
Mar 31 17:29:29 mirtuel sshd[424]: Server listening on 0.0.0.0 port 22.
Mar 31 17:29:29 mirtuel sshd[424]: Server listening on :: port 22.
Mar 31 17:30:10 mirtuel sshd[1147]: Accepted password for roland from 192.168.1.129 port 38742 ssh2
Mar 31 17:30:10 mirtuel sshd[1147]: pam_unix(sshd:session): session opened for user roland by (uid=0)
Mar 31 17:30:35 mirtuel sshd[1180]: /etc/ssh/sshd_config line 28: unsupported option "yess".
Mar 31 17:30:35 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:35 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:35 mirtuel sshd[1182]: /etc/ssh/sshd_config line 28: unsupported option "yess".
Mar 31 17:30:35 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:35 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:35 mirtuel sshd[1184]: /etc/ssh/sshd_config line 28: unsupported option "yess".
Mar 31 17:30:35 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:35 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:36 mirtuel sshd[1186]: /etc/ssh/sshd_config line 28: unsupported option "yess".
Mar 31 17:30:36 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:36 mirtuel sshd[1188]: /etc/ssh/sshd_config line 28: unsupported option "yess".
Mar 31 17:30:36 mirtuel systemd[1]: ssh.service: main process exited, code=exited, status=255/n/a
Mar 31 17:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
Mar 31 17:30:36 mirtuel systemd[1]: ssh.service start request repeated too quickly, refusing to start.
Mar 31 17:30:36 mirtuel systemd[1]: Failed to start OpenBSD Secure Shell server.
Mar 31 17:30:36 mirtuel systemd[1]: Unit ssh.service entered failed state.
#
vi /etc/ssh/sshd_config
#
systemctl start ssh.service
#
systemctl status ssh.service
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/lib/systemd/system/ssh.service; enabled)
Active: active (running) since Tue 2015-03-31 17:31:09 CEST; 2s ago
Process: 1023 ExecReload=/bin/kill -HUP $MAINPID (code=exited, status=0/SUCCESS)
Main PID: 1222 (sshd)
CGroup: /system.slice/ssh.service
└─1222 /usr/sbin/sshd -D
#
After checking the status of the service (failed), we went on to check the logs; they indicate an error in the configuration file. After editing the configuration file and fixing the error, we restart the service, then verify that it is indeed running.
9.1.2. The System V init system
The System V init system (which we'll call init for brevity) executes several processes, following instructions from the
/etc/inittab
file. The first program that is executed (which corresponds to the
sysinit step) is
/etc/init.d/rcS
, a script that executes all of the programs in the
/etc/rcS.d/
directory.
Среди них можно найти последовательность программ, отвечающих за:
настройку клавиатуры в консоли;
загрузку драйверов: большая часть модулей ядра загружается самим ядром при обнаружении оборудования; дополнительные драйверы затем загружаются автоматически, если соответствующие модули указаны в /etc/modules
;
проверку целостности файловых систем;
монтирование локальных разделов;
настройку сети;
монтирование сетевых файловых систем (NFS).
After this stage, init
takes over and starts the programs enabled in the default runlevel (which is usually runlevel 2). It executes /etc/init.d/rc 2
, a script that starts all services which are listed in /etc/rc2.d/
and whose names start with the “S” letter. The two-figures number that follows had historically been used to define the order in which services had to be started, but nowadays the default boot system uses insserv
, which schedules everything automatically based on the scripts' dependencies. Each boot script thus declares the conditions that must be met to start or stop the service (for example, if it must start before or after another service); init
then launches them in the order that meets these conditions. The static numbering of scripts is therefore no longer taken into consideration (but they must always have a name beginning with “S” followed by two digits and the actual name of the script used for the dependencies). Generally, base services (such as logging with rsyslog
, or port assignment with portmap
) are started first, followed by standard services and the graphical interface (gdm3
).
Такая основанная на зависимостях система загрузки делает возможной автоматизацию смены нумерации, которая была бы весьма утомительной, если бы её приходилось выполнять вручную, и снижает риск человеческой ошибки, поскольку планирование выполняется в соответствии с формальными параметрами. Другим преимуществом является возможность параллельного запуска сервисов, независимых друг от друга, что может ускорить процесс загрузки.
init
различает несколько уровней запуска, так что она может переключаться с одного на другой при посредстве команды telinit новый-уровень
. init
сразу же запускает /etc/init.d/rc
заново с новым уровнем запуска. Этот сценарий после этого запускает недостающие сервисы и останавливает те, которые более не нужны. Для этого он руководствуется содержимым /etc/rcX.d
(где X означает новый уровень запуска). Сценарии, начинающиеся с «S» (как в слове «Start») — это сервисы, которые должны быть запущены; те, что начинаются с «K» (как в слове «Kill») — сервисы, которые должны быть остановлены. Сценарий не запускает никаких сервисов, которые уже были активированы на прежнем уровне запуска.
By default, System V init in Debian uses four different runlevels:
Уровень 0 используется только временно, при выключении питания компьютера. Поэтому он содержит только «K»-сценарии.
Уровень 1, также известный как однопользовательский режим, соответствует системе с урезанной функциональностью; он включает только основные сервисы и предназначается для операций по обслуживанию, когда взаимодействие с обычными пользователями нежелательно.
Уровень 2 — уровень для нормальной работы, включающий сетевые сервисы, графический интерфейс, вход пользователей и т. п.
Уровень 6 похож на уровень 0 с той разницей, что он используется во время остановки системы перед перезагрузкой.
Есть и другие уровни, в частности с 3 по 5. По умолчанию они настроены, чтобы работать точно так же, как уровень 2, но администратор может изменить их (путём добавления или удаления сценариев в соответствующие каталоги /etc/rcX.d
), чтобы приспособить их под свои специфические нужды.
Все сценарии, содержащиеся в различных каталогах /etc/rcX.d
на самом деле являются лишь символьными ссылками — созданными при установке пакета программой update-rc.d
— указывающими на сами сценарии, хранящиеся в /etc/init.d/
. Администратор может настроить доступность сервисов на каждом уровне запуска путём повторного запуска update-rc.d
с изменёнными параметрами. На странице руководства update-rc.d(8) подробно описан синтаксис. Обратите внимание, что удаление всех символьных ссылок (с помощью параметра remove
) — не лучший метод отключения сервиса. Вместо этого следует просто настроить, чтобы он не запускался на нужном уровне запуска (сохранив соответствующие вызовы для остановки его в случае, если сервис работал на предыдущем уровне запуска). Поскольку интерфейс update-rc.d
несколько запутанный, может оказаться более удобным использовать rcconf
(из пакета rcconf), интерфейс которой более дружествен к пользователю.
Наконец, init
запускает программу управления виртуальными консолями (getty
). Она выводит приглашение, ожидает ввода имени пользователя, а затем выполняет login пользователь
, чтобы начать сессию.