Глава 9. systemd (Часть II)
Содержание
На данный момент мы достигли своей службы dracut.pre-mount.service
,
когда наша корневая файловая система пользователя ещё не смонтирована внутри initramfs. Следующая стадия запуска
systemd смонтирует эту корневую файловую систему в sysroot
.
systemd принимает параметр командной строки dracut mount
, который
сбрасывает нас в аварийную оболочку -mount
. Как вы видите на
Рисунке 9-1,
мы передали параметр командной строки ядра rd.break=mount
.
Как вы можете видеть на
Рисунке 9-2,
sysroot
была смонтирована с корневой файловой системой пользователя в режиме
только для чтения.
Особая точка входа dracut.mount
(usr/lib/systemd/system/dracut-mount.service
) запустит надлежащий сценарий
/bin/dracut-mount
из initramfs, который и выполнит необходимую часть
монтирования.
#vim usr/lib/systemd/system/dracut-mount.service
Как вы можете видеть, этот выполняет сценарий dracut-mount
из iniramfs
и к тому же экспортирует соответствующую переменную NEWROOT
со значением
sysroot
.
Environment=NEWROOT=/sysroot
ExecStart=-/bin/dracut-mount
[Unit]
Description=dracut mount hook
Documentation=man:dracut-mount.service(8)
After=initrd-root-fs.target initrd-parse-etc.service
After=dracut-initqueue.service dracut-pre-mount.service
ConditionPathExists=/usr/lib/initrd-release
ConditionDirectoryNotEmpty=|/lib/dracut/hooks/mount
ConditionKernelCommandLine=|rd.break=mount
DefaultDependencies=no
Conflicts=shutdown.target emergency.target
[Service]
Environment=DRACUT_SYSTEMD=1
Environment=NEWROOT=/sysroot
Type=oneshot
ExecStart=-/bin/dracut-mount
StandardInput=null
StandardOutput=syslog
StandardError=syslog+console
KillMode=process
RemainAfterExit=yes
KillSignal=SIGHUP
#vim bin/dracut-mount
1 #!/usr/bin/sh
2 export DRACUT_SYSTEMD=1
3 if [ -f /dracut-state.sh ]; then
4 . /dracut-state.sh 2>/dev/null
5 fi
6 type getarg >/dev/null 2>&1 || . /lib/dracut-lib.sh
7
8 source_conf /etc/conf.d
9
10 make_trace_mem "hook mount" '1:shortmem' '2+:mem' '3+:slab'
11
12 getarg 'rd.break=mount' -d 'rdbreak=mount' && emergency_shell -n mount "Break mount"
13 # mount scripts actually try to mount the root filesystem, and may
14 # be sourced any number of times. As soon as one suceeds, no more are sourced.
15 i=0
16 while :; do
17 if ismounted "$NEWROOT"; then
18 usable_root "$NEWROOT" && break;
19 umount "$NEWROOT"
20 fi
21 for f in $hookdir/mount/*.sh; do
22 [ -f "$f" ] && . "$f"
23 if ismounted "$NEWROOT"; then
24 usable_root "$NEWROOT" && break;
25 warn "$NEWROOT has no proper rootfs layout, ignoring and removing offending mount hook"
26 umount "$NEWROOT"
27 rm -f -- "$f"
28 fi
29 done
30
31 i=$(($i+1))
32 [ $i -gt 20 ] && emergency_shell "Can't mount root filesystem"
33 done
34
35 export -p > /dracut-state.sh
36
37 exit 0
В Главе 8 мы увидели как именно это сбрасывает нас в некую
аварийную оболочку и все связанные функции этого. Так как мы остановили свою последовательность запуска после
монтирования необходимой корневой файловой системы внутри initramfs, как вы можете обнаружить на
Рисунке 9-3,
наш systemd-fstab-generator
уже был выполнен и уже были созданы файлы элементов
-mount
.
Помните, что значение названия корневой файловой системы в sysroot.mount
было взято из соответствующего файла /proc/cmdline
. Сам
sysroot.mount
чётко указывает что следует монтировать и где это надлежит
монтировать.
Как мы уже говорили много раз, конечная цель рассматриваемой последовательности запуска состоит в предоставлении корневой файловой системы пользователя самому пользователю и при выполнении этого основные достигаемые systemd стадии таковы:
-
Поиск самой корневой файловой системы пользователя.
-
Монтирование этой корневой файловой системы пользователя (мы достигли этого этапа запуска).
-
Поиск прочих необходимых файловых систем и их монтирования (
usr
,var
,nfs
,cifs
и т.п.). -
Переключение на смонтированную корневую файловую систему пользователя.
-
Запуск необходимых демонов пространства пользователя.
-
Запуск
multi-user.target
илиgraphical.target
(что выходит за рамки этой книги).
Как вы можете видеть, на данный момент мы достигли этапа 2, коим выступает монтирование необходимой корневой
файловой системы пользователя. Мы знаем, что systemd имеет .targets
, а
.target
это ни что иное, как пакет файлов элементов. Такой
.target
может быть успешно запущен только когда все эти файлы элементов
были успешно запущены.
В установленном мире systemd имеется моножество целей, такие как basic.target
,
multi-user.target
, graphical.target
,
default.target
и sysinit.target
,
перечисляя лишь часть. Окончательная цель initramfs
состоит в достижении
initrd.target
. После того как успешно запущен
initrd.target
, тогда в него переключится switch_root
systemd. Итак, вначале мы взглянем на initrd.target
и где она стоит с точки
зрения последовательности запуска. Обращаем ваше внимание на показанную на
Рисунке 9-4
блок- схему.
Когда вы находитесь вне initramfs (то есть после switch_root
),
default.target
systemd будет либо multi-user.target
,
либо graphical.target
, в то время как внутри initremfs (то есть паеред
switch_root
) после basic.target
default.target
systemd будет initrd.target
.
Итак, после успешного завершения sysinit.target
и
basic.target
, основная задача systemd состоит в достижении
initrd.target
. Для её достижения systemd применит этап
sysroot.target
для считывания тех смонтированных файлов элементов, которые
созданы systemd-fstab-generator
. Сама служба
dracut-mount.service
смонтирует необходимую корневую файловую систему в
/sysroot
и затем systemd исполнит соответствующую службу
initrd-parse-etc.service
. Она выполнит синтаксический разбор файла
/sysroot/etc/fstab
и создаст необходимые монтируемые файлы элементов для
usr
или всех прочих точек монтирования, которые обладают установкой параметра
x-initrd.mount
. Вот как работает
initrd-parse-etc.service
:
# cat usr/lib/systemd/system/initrd-parse-etc.service | grep -v '#'
[Unit]
Description=Reload Configuration from the Real Root
DefaultDependencies=no
Requires=initrd-root-fs.target
After=initrd-root-fs.target
OnFailure=emergency.target
OnFailureJobMode=replace-irreversibly
ConditionPathExists=/etc/initrd-release
[Service]
Type=oneshot
ExecStartPre=-/usr/bin/systemctl daemon-reload
ExecStart=-/usr/bin/systemctl --no-block start initrd-fs.target
ExecStart=/usr/bin/systemctl --no-block start initrd-cleanup.service
Обычно эта служба выполняет systemctl
с неким переключателем
daemon-reload
. Это перезапускает установленную конфигурацию диспетчера
systemd. Что перезапустит все генераторы, перезагрузит все файлы элементов и повторно создаст всё дерево
зависимостей целиком. В то время как этот демон перезапускается, будут оставаться доступными все сокеты, в которых
systemd от имени пользователя выполняет ожидание. Те генераторы, которые повторно исполняет systemd таковы:
# ls usr/lib/systemd/system-generators/ -l
total 92
-rwxr-xr-x. 1 root root 3750 Jan 10 19:18 dracut-rootfs-generator
-rwxr-xr-x. 1 root root 45640 Dec 21 12:19 systemd-fstab-generator
-rwxr-xr-x. 1 root root 37032 Dec 21 12:19 systemd-gpt-auto-generator
Как вы можете видеть, это выполнит systemd-fstab-generator
, который считает
записи /sysroot/etc/fstab
и создаст необходимые монтируемые файлы элементов
для usr
и для устройств, обладающих установленным параметром
x-initrd.mount
. Короче говоря,
systemd-fstab-generator
исполняется дважды.
Поэтому когда вы сваливаете себя в соответствующую оболочку монтирования
(rd.break=mount
), вы на самом деле прерываете свою последовательность
запуска после соответствующей initrd.target
. Эта цель запускает всего лишь
такие службы:
# ls usr/lib/systemd/system/initrd.target.wants/
dracut-cmdline-ask.service dracut-mount.service dracut-pre-trigger.service
dracut-cmdline.service dracut-pre-mount.service dracut-pre-udev.service
dracut-initqueue.service dracut-pre-pivot.service
Для понимания всего этого обращаем ваше внимание на Рисунок 9-5.
Теперь мы достигли окончательного этапа запуска systemd, коим выступает switch_root
.
Systemd переключает свою корневую файловую систему с initramfs (/
) на
смонтированную корневую файловую систему пользователя (/sysroot
). Systemd
достигает этого предпринимая следующие шаги:
-
Монтируя соответствующую новую корневую файловую систему (
/sysroot
) -
Превращая её в свою корневую файловую систему (
/
) -
Удаляя весь доступ к старой (initramfs) корневой файловой системе
-
Отключая свою файловую систему initramfs и высвобождая файловую систему ramfs
Имеются три основных момента, которые мы обсудим в данной главе:
-
switch_root
: Мы изучим это старым способомinit
. -
pivot_root
: Мы исследуем это вариантомsystemd
. -
chroot
: Это мы поясняем в Главе 10.
Основанная на init система применяет switch_root
для переключения на
некую новую корневую файловую систему (sysroot
). Основная цель
switch_root
достаточно хорошо поясняется в её странице руководства, отображаемой
здесь:
#man switch_root
NAME
switch_root - переключиться на другую файловую систему в качестве корня всего монтируемого дерева
SYNOPSIS
switch_root [-hV]
switch_root newroot init [arg...]
DESCRIPTION
switch_root перемещает уже смонтированные /proc, /dev, /sys and /run в newroot и превращает newroot в свою новую корневую файловую систему и запускает процесс init.
WARNING: switch_root рекурсивно удаляет все файлы и каталоги в своей текущей корневой файловой системе.
OPTIONS
-h, --help
Отображает текст подсказки и выполняет выход.
-V, --version
Отображает сведения о версии и выполняет выход.
RETURN VALUE
switch_root возвращает 0 в случае успеха и 1 при неудаче.
NOTES
switch_root отказывает в работе когда newroot не выступает установленным корнем монтирования. Если вы желаете переключить корень в нек4ий каталог, который не соответствует данному требованию, тогда вы вначале можете применить трюк со связываением- монтироованием для включения любого каталога в некую точку монтирования:
mount --bind $DIR $DIR
Итак, это переключает в некую новую корневую файловую систему (sysroot
)
и совместно с самим корнем это перемещает все старые виртуальные фаловые системы корневой файловой системы
(proc
, dev, sys
,
sys
и т.п.) в свой новый корень. Наилучшая функциональная возможность
switch_root
состоит в том, что после монтирования своей новой корневой
файловой системы она запускает сам процесс init
в ней самой. Переключение в
новую корневую файловую систему имеет место в исходном коде dracut. Самой
latest
версией dracut на момент написания этих строк была
049
. Функция switch_root
определяется
в файле dracut-049/modules.d/99base/init.sh
.
387 unset PS4
388
389 CAPSH=$(command -v capsh)
390 SWITCH_ROOT=$(command -v switch_root)
391 PATH=$OLDPATH
392 export PATH
393
394 if [ -f /etc/capsdrop ]; then
395 . /etc/capsdrop
396 info "Calling $INIT with capabilities $CAPS_INIT_DROP dropped."
397 unset RD_DEBUG
398 exec $CAPSH --drop="$CAPS_INIT_DROP" -- \
399 -c "exec switch_root \"$NEWROOT\" \"$INIT\" $initargs" || \
400 {
401 warn "Command:"
402 warn capsh --drop=$CAPS_INIT_DROP -- -c exec switch_root "$NEWROOT" "$INIT" $initargs
403 warn "failed."
404 emergency_shell
405 }
406 else
407 unset RD_DEBUG
408 exec $SWITCH_ROOT "$NEWROOT" "$INIT" $initargs || {
409 warn "Something went very badly wrong in the initramfs. Please "
410 warn "file a bug against dracut."
411 emergency_shell
412 }
413 fi
В нашем предыдущем коде вы можете обнаружить, что exec switch_root
вызывалось в точности так, как это было описано в странице руководства switch_root
.
Определяемыми значениями переменных для NEWROOT
и
INIT
выступают такие:
NEWROOT = "/sysroot"
INIT = 'init' or 'sbin/init'
Просто для вашего сведения, в наши дни этот файл init
выступает
symlink
для systemd
.
# ls -l sbin/init
lrwxrwxrwx. 1 root root 22 Dec 21 12:19 sbin/init -> ../lib/systemd/systemd
Для успешного switch_root
всех виртуальных файловых систем они прежде
всего должны быть смонтированы. Это достигается через
dracut-049/modules.d/99base/init.sh
. Имеются определённые этапы и они
таковы:
-
Смонтировать файловую систему
proc
. -
Смонтировать файловую систему
sys
. -
Смонтировать каталог
/dev
сdevtmpfs
. -
Вручную создать файлы устройств
stdin
,stdout
,stderr
,pts
иshm
. -
Создать точку монтирования
/run
с tmpfs в ней. (Такая точка монтирования/run
не доступна в системах на основеinit
).
#vim dracut-049/modules.d/99base/init.sh
11 NEWROOT="/sysroot"
12 [ -d $NEWROOT ] || mkdir -p -m 0755 $NEWROOT
13
14 OLDPATH=$PATH
15 PATH=/usr/sbin:/usr/bin:/sbin:/bin
16 export PATH
17
18 # mount some important things
19 [ ! -d /proc/self ] && \
20 mount -t proc -o nosuid,noexec,nodev proc /proc >/dev/null
21
22 if [ "$?" != "0" ]; then
23 echo "Cannot mount proc on /proc! Compile the kernel with CONFIG_PROC_FS!"
24 exit 1
25 fi
26
27 [ ! -d /sys/kernel ] && \
28 mount -t sysfs -o nosuid,noexec,nodev sysfs /sys >/dev/null
29
30 if [ "$?" != "0" ]; then
31 echo "Cannot mount sysfs on /sys! Compile the kernel with CONFIG_SYSFS!"
32 exit 1
33 fi
34
35 RD_DEBUG=""
36 . /lib/dracut-lib.sh
37
38 setdebug
39
40 if ! ismounted /dev; then
41 mount -t devtmpfs -o mode=0755,noexec,nosuid,strictatime devtmpfs /dev >/dev/null
42 fi
43
44 if ! ismounted /dev; then
45 echo "Cannot mount devtmpfs on /dev! Compile the kernel with CONFIG_DEVTMPFS!"
46 exit 1
47 fi
48
49 # prepare the /dev directory
50 [ ! -h /dev/fd ] && ln -s /proc/self/fd /dev/fd >/dev/null 2>&1
51 [ ! -h /dev/stdin ] && ln -s /proc/self/fd/0 /dev/stdin >/dev/null 2>&1
52 [ ! -h /dev/stdout ] && ln -s /proc/self/fd/1 /dev/stdout >/dev/null 2>&1
53 [ ! -h /dev/stderr ] && ln -s /proc/self/fd/2 /dev/stderr >/dev/null 2>&1
54
55 if ! ismounted /dev/pts; then
56 mkdir -m 0755 /dev/pts
57 mount -t devpts -o gid=5,mode=620,noexec,nosuid devpts /dev/pts >/dev/null
58 fi
59
60 if ! ismounted /dev/shm; then
61 mkdir -m 0755 /dev/shm
62 mount -t tmpfs -o mode=1777,noexec,nosuid,nodev,strictatime tmpfs /dev/shm >/dev/null
63 fi
64
65 if ! ismounted /run; then
66 mkdir -m 0755 /newrun
67 if ! str_starts "$(readlink -f /bin/sh)" "/run/"; then
68 mount -t tmpfs -o mode=0755,noexec,nosuid,nodev,strictatime tmpfs /newrun >/dev/null
69 else
70 # the initramfs binaries are located in /run, so don't mount it with noexec
71 mount -t tmpfs -o mode=0755,nosuid,nodev,strictatime tmpfs /newrun >/dev/null
72 fi
73 cp -a /run/* /newrun >/dev/null 2>&1
74 mount --move /newrun /run
75 rm -fr -- /newrun
76 fi
Необходимые шаги почти такие же как и те, что мы обсуждали для систем на основании init
.
Единственным отличием для systemd
является то, что его исполняемый код сделан
на C. Поэтому, очевидно, переключение своего корня имеет место в исходном коде C systemd, что отражено тут:
src/shared/switch-root.c:
Вначале рассмотрим следующее:
new_root = sysroot
old_root = /
Это переместит все те виртуальные файловые системы, которыми уже была наполнена корневая файловая система
initramfs; затем функция path_equal
проверит доступен ли путь
new_root
.
if (path_equal(new_root, "/"))
return 0;
Позднее вызывается syscall pivot_root
(init
применяет switch_root
) и изменяет текущий корень с
/
(корневой файловой системы самой initramfs) на
sysroot
(настраиваемая корневая файловая система пользователя).
pivot_root(new_root, resolved_old_root_after) >= 0)
Прежде чем мы двинемся далее, нам требуется разобраться с тем что из себя представляет
pivot_root
и что она делает.
# man pivot_root
NAME
pivot_root - изменяет текущую корневую файловую систему
SYNOPSIS
pivot_root new_root put_old
DESCRIPTION
pivot_root перемещает собственную корневую файловую систему текущего процесса в каталог put_old и создаёт новую корневую файловую систему new_root. Поскольку pivot_root(8) просто вызывает pivot_root(2), для дополнительных сведений мы отсылаем к соответствующей странице руководства:
Обратите внимание, что в зависимости от реализации pivot_root, root и cwd вызывающей стороны могут измениться, а могут остаться прежними. Ниже приводится последовательность для вызова pivot_root, которая работает в любом случае в предположении что pivot_root и chroot пребывают в текущем PATH:
cd new_root
pivot_root . put_old
exec chroot . command
Заметьте, что chroot обязан быть доступным в своём старом корне, а также и в новом корне, ибо pivot_root может обладать, а может и нет в явном виде изменённым значением корневого каталога своей оболочки.
Обратите внимание, что выполнение chroot изменяет запущенный исполняемый файл, что необходимо когда его старый корневой каталог должен быть размонтирован после всего. Также отметьте, что стандартные input, output и error могут всё ещё указывать на некое устройство своей старой корневой файловой системе, сохраняя их занятыми. Их запросто можно заменить при вызове chroot (см. далее; обратите внимание на отсутствие начальных косых черт, чтобы это работало независимо от того изменил pivot_root корень своей оболочки или нет).
pivot_root
изменяет свою корневую файловую систему (корневую файловую
систему самого initramfs) текущего процесса (systemd) на новую корневую файловую систему
(sysroot
), а также он изменяет сам запущенный исполняемый файл
(systemd из initramfs) на новый (systemd из корневой файловой системы пользователя).
После pivot_root
, выполняется отключение старого корневого устройства
initramfs (src/shared/switch-root.c
).
# vim src/shared/switch-root.c
96 /* Вначале мы пробуем pivot_root() с тем, чтобы мы смогли размонтировать свой старый корневой каталог. Во многих случаях (т.е. когда rootfs выступает /),
97 * это, однако, невозможно и следовательно мы просто повторно монтируем корень */
98 if (pivot_root(new_root, resolved_old_root_after) >= 0) {
99
100 /* Немедленно избавляемся от своего старого корня, когда установлен detach_oldroot.
101 * Поскольку мы покидаем его, нам надлежит делать это не спеша. */
102 if (unmount_old_root) {
103 r = umount_recursive(old_root_after, MNT_DETACH);
104 if (r < 0)
105 log_warning_errno(r, "Failed to unmount old root directory tree, ignoring: %m");
106 }
107
108 } else if (mount(new_root, "/", NULL, MS_MOVE, NULL) < 0)
109 return log_error_errno(errno, "Failed to move %s to /: %m", new_root);
110
После успешного pivot_root
вот наше текущее состояние:
-
sysroot
превратился в корень (/
). -
Текущим рабочим каталогом стал корень (
/
). -
Будет исполнен
chroot
с тем, чтобы bash изменил свой корневой каталог со старого корня (initramfs) на новую (пользовательскую) корневую файловую систему.chroot
будет обсуждён в нашей следующекй главе.
Наконец, удаляем устройство old_root
(rm -rf
).
110
111 if (chroot(".") < 0)
112 return log_error_errno(errno, "Failed to change root: %m");
113
114 if (chdir("/") < 0)
115 return log_error_errno(errno, "Failed to change directory: %m");
116
117 if (old_root_fd >= 0) {
118 struct stat rb;
119
120 if (fstat(old_root_fd, &rb) < 0)
121 log_warning_errno(errno, "Failed to stat old root directory, leaving: %m");
122 else
123 (void) rm_rf_children(TAKE_FD(old_root_fd), 0, &rb); /* takes possession of the dir fd, even on failure */
124 }
Для лучшего понимания я настоятельно рекомендую прочесть весь исходный код
src/shared/switch-root.c
, отображаемый здесь:
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <limits.h>
6 #include <stdbool.h>
7 #include <sys/mount.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10
11 #include "base-filesystem.h"
12 #include "fd-util.h"
13 #include "fs-util.h"
14 #include "log.h"
15 #include "missing_syscall.h"
16 #include "mkdir.h"
17 #include "mount-util.h"
18 #include "mountpoint-util.h"
19 #include "path-util.h"
20 #include "rm-rf.h"
21 #include "stdio-util.h"
22 #include "string-util.h"
23 #include "strv.h"
24 #include "switch-root.h"
25 #include "user-util.h"
26 #include "util.h"
27
28 int switch_root(const char *new_root,
29 const char *old_root_after, /* path below the new root, where to place the old root after the transition */
30 bool unmount_old_root,
31 unsigned long mount_flags) { /* MS_MOVE or MS_BIND */
32
33 _cleanup_free_ char *resolved_old_root_after = NULL;
34 _cleanup_close_ int old_root_fd = -1;
35 bool old_root_remove;
36 const char *i;
37 int r;
38
39 assert(new_root);
40 assert(old_root_after);
41
42 if (path_equal(new_root, "/"))
43 return 0;
44
45 /* Check if we shall remove the contents of the old root */
46 old_root_remove = in_initrd();
47 if (old_root_remove) {
48 old_root_fd = open("/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
49 if (old_root_fd < 0)
50 return log_error_errno(errno, "Failed to open root directory: %m");
51 }
52
53 /* Determine where we shall place the old root after the transition */
54 r = chase_symlinks(old_root_after, new_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved_old_root_after, NULL);
55 if (r < 0)
56 return log_error_errno(r, "Failed to resolve %s/%s: %m", new_root, old_root_after);
57 if (r == 0) /* Doesn't exist yet. Let's create it */
58 (void) mkdir_p_label(resolved_old_root_after, 0755);
59
60 /* Work-around for kernel design: the kernel refuses MS_MOVE if any file systems are mounted MS_SHARED. Hence
61 * remount them MS_PRIVATE here as a work-around.
62 *
63 * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */
64 if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0)
65 return log_error_errno(errno, "Failed to set \"/\" mount propagation to private: %m");
66
67 FOREACH_STRING(i, "/sys", "/dev", "/run", "/proc") {
68 _cleanup_free_ char *chased = NULL;
69
70 r = chase_symlinks(i, new_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &chased, NULL);
71 if (r < 0)
72 return log_error_errno(r, "Failed to resolve %s/%s: %m", new_root, i);
73 if (r > 0) {
74 /* Already exists. Let's see if it is a mount point already. */
75 r = path_is_mount_point(chased, NULL, 0);
76 if (r < 0)
77 return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", chased);
78 if (r > 0) /* If it is already mounted, then do nothing */
79 continue;
80 } else
81 /* Doesn't exist yet? */
82 (void) mkdir_p_label(chased, 0755);
83
84 if (mount(i, chased, NULL, mount_flags, NULL) < 0)
85 return log_error_errno(errno, "Failed to mount %s to %s: %m", i, chased);
86 }
87
88 /* Do not fail if base_filesystem_create() fails. Not all switch roots are like base_filesystem_create() wants
89 * them to look like. They might even boot, if they are RO and don't have the FS layout. Just ignore the error
90 * and switch_root() nevertheless. */
91 (void) base_filesystem_create(new_root, UID_INVALID, GID_INVALID);
92
93 if (chdir(new_root) < 0)
94 return log_error_errno(errno, "Failed to change directory to %s: %m", new_root);
95
96 /* We first try a pivot_root() so that we can umount the old root dir. In many cases (i.e. where rootfs is /),
97 * that's not possible however, and hence we simply overmount root */
98 if (pivot_root(new_root, resolved_old_root_after) >= 0) {
99
100 /* Immediately get rid of the old root, if detach_oldroot is set.
101 * Since we are running off it we need to do this lazily. */
102 if (unmount_old_root) {
103 r = umount_recursive(old_root_after, MNT_DETACH);
104 if (r < 0)
105 log_warning_errno(r, "Failed to unmount old root directory tree, ignoring: %m");
106 }
107
108 } else if (mount(new_root, "/", NULL, MS_MOVE, NULL) < 0)
109 return log_error_errno(errno, "Failed to move %s to /: %m", new_root);
110
111 if (chroot(".") < 0)
112 return log_error_errno(errno, "Failed to change root: %m");
113
114 if (chdir("/") < 0)
115 return log_error_errno(errno, "Failed to change directory: %m");
116
117 if (old_root_fd >= 0) {
118 struct stat rb;
119
120 if (fstat(old_root_fd, &rb) < 0)
121 log_warning_errno(errno, "Failed to stat old root directory, leaving: %m");
122 else
123 (void) rm_rf_children(TAKE_FD(old_root_fd), 0, &rb); /* takes possession of the dir fd, even on failure */
124 }
125
126 return 0;
127 }
Здесь мы успешно переключились на корневую файловую систему пользователя и покинули свою среду initramfs. Теперь
начинает выполнение с PID 1
systemd из корневой файловой системы пользователя и
он заботится обо всей остающейся процедуре запуска, которая такова:
-
systemd запускает необходимые службы пространства пользователя, такие как
httpd
,mysql
,postfix
,network services
и т.п. -
В конце концов основная цель состоит в достижении
default.target
. Как мы обсуждали ранее, передswitch_root
, той целью, которая вызываетсяdefault.target
systemd будетinitrd.target
, а послеswitch_root
ею будет либоmulti-user.target
, либоgraphical.target
.
Но что происходит с тем имеющимся процессом systemd
, который стартовал
из initramfs (своей корневой файловой системы)? Убивается ли он после switch_root
или pivot_root
? Запускается ли из созданной корневой файловой системы
пользователя новый systemd
?
Ответ прост.
-
systemd initramfs создаёт некий конвейер (
pipe
). -
systemd ветвится (
forks
). -
Первоначальный
PID 1
выполняетchroot
в/systemd
и исполняет/systemd/usr/lib/systemd/systemd
. -
Разветвлённый systemd последовательно упорядочивает своё состояние в конвейер в
PID 1
и выполняет выход. -
PID 1
выполняет преобразование упорядоченных данных из конвейера и продолжает свою работы с обновлённой конфигурацией в/
(который ранее был/sysroot
).
Надеюсь, вы удовлетворены знакомством systemd внутри initramfs. Как мы уже упоминали ранее, вся остающаяся последовательность запуска systemd, которая будет происходить вне initramfs, будет более или менее походить на то, что мы обсуждали до сих пор.
Как запускается GUI выходит за рамки данной книги. В своей следующей главе мы обсудим образы live ISO, а также аварийный режим.