systemdとは
systemdはカーネルが生成する最初のユーザプロセスで、Linuxの起動処理やシステム全体の管理を行います。
プロセス番号PID=1で
、デーモン(メモリー上に常に待機している常駐プログラム)です。
要するに、パソコンの電源ボタン押してからシステムが起動するまでの間で誰よりも先人に立って
仕事をこなしていくリーダー的な存在です。
GRUB2によってHDDからメモリーにロードされたカーネルは、H/Wを認識できるようになった後、
PID=1のsystemdを起動する。
GRUB2はこちらの記事参照→作成中
【システム起動の流れ】
systemd のプロセス番号がPID=1であることを確認します
# ps aux | head -n 10
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.3 175684 13976 ? Ss 18:41 0:02 /usr/lib/systemd/systemd --switched-root --system --deserialize 17
root 2 0.0 0.0 0 0 ? S 18:41 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? I< 18:41 0:00 [rcu_gp]
root 4 0.0 0.0 0 0 ? I< 18:41 0:00 [rcu_par_gp]
root 6 0.0 0.0 0 0 ? I< 18:41 0:00 [kworker/0:0H-events_highpri]
root 9 0.0 0.0 0 0 ? I< 18:41 0:00 [mm_percpu_wq]
root 10 0.0 0.0 0 0 ? S 18:41 0:00 [rcu_tasks_rude_]
root 11 0.0 0.0 0 0 ? S 18:41 0:00 [rcu_tasks_trace]
root 12 0.0 0.0 0 0 ? S 18:41 0:00 [ksoftirqd/0]
systemdは【Unit】という単位でシステム起動処理をしている。
linuxを起動してしただけなのに、カーネルが既に218個の処理をメモリ上でしています
(電源を入れただけなのに裏ではでいろんな処理がされてるんですね)
従来のinit/upstartからsystemdへ
技術の進歩を振り返ると理解が深まるので軽く触れます。
CentOS6以前(2012年頃)までは、Linuxの起動処理はinit/upstartと呼ばれる仕組みで行われていました。
これが、CentOS7(2015年)以降全く違う新しい仕組みのsystemdに置き換わる。
systemdはinit以前の起動プロセスに影響しない。
図だけ見るとあまり変わっていないように見えるかもしれないが、
これは全く違う仕組みで起動処理にかかる速度も大幅に改善した。
今回は、systemdの動作の基礎となる【Unit】の概念を焦点を当てる。
initのデメリット
従来のinitの起動プロセスは、スクリプトで上から順番に直線的に起動していくので、
上流の起動で手間とると、以降のすべての実行するサービスが待たされてしまい時間がかかる。
systemdのメリット
従来のスクリプトを廃止し、【Unit】という単位で処理を管理する。
これまで/etc/rc.d/配下のスクリプトが実施していた処理の内容は、
全てUnitとして定義され直接systemdがunitを起動する。以下のプロセスを見ても、
initに比べて直線的ではなく、並列に起動しているのがわかる。
Unitの特徴
①スクリプトではなく設定ファイル
→Unitの設定をもとにsystemd自身が処理を実行する
②Unit間の依存・順序関係(並列処理)の定義
→「Aプロセス」を起動するには「Bプロセス」が必要など。
Unitは、「target」「mount」「service」「device」など、役割によってタイプがわかれていて、それぞれのunitは、依存関係が定義されている。
最初のプロセスPID=1としてsystemdが起動すると、「default.target」というUnitを頂点とする、依存関係のツリーを構築した後、依存するUnitを起動していく。
$ vi /etc/systemd/system/default.target
[Unit]
Description=Graphical Interface
Documentation=man:systemd.special(7)
Requires=multi-user.target
Wants=display-manager.service
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target display-manager.service
AllowIsolate=yes
上の場合、【requires=multi-user.target】と記述がある。
この意味は【default.target】と【multi-user.target】が必ず同時起動しないといけないという依存の定義である。Unit間の依存・順序関係の定義は以下を参照する。
このUnit間の依存関係の定義をdefault.targetの各々のunitと見比べると、
どういう依存関係になっているかが理解できる。ここからわかることが、
systemdは順序関係の情報をもとにして、複数のUnitをできるかぎり並列に起動していきます。
このように、systemdを利用すると、
・シェルスクリプトを使わずにsystemdが直接Unit(サービス)を起動する。
・Unitの起動処理を可能な限り並列化する。
・Unitの起動をオンデマンド化する。
という工夫にによって、システムの起動時間が圧倒的に短縮されます。
Unitの定義ファイルの配置ディレクトリは2か所
① /usr/lib/systemd/system/
システムのデフォルトの設定:
インストール済の RPM PKGで配布されたUnitの設定ファイル
② /etc/systemd/system/
ユーザ独自設定(管理者が作成・管理するUnitの設定ファイル):
同名のファイルをここに配置するとこちらのファイルが優先される
用途別に様々な種類のUnitが存在
これはシステム起動状態を表す従来のrunlevelとtargetの対応表であるが、コマンドでも確認できる。
lrwxrwxrwx. 1 root root 15 9月 13 17:03 /usr/lib/systemd/system/runlevel0.target -> poweroff.target
lrwxrwxrwx. 1 root root 13 9月 13 17:03 /usr/lib/systemd/system/runlevel1.target -> rescue.target
lrwxrwxrwx. 1 root root 17 9月 13 17:03 /usr/lib/systemd/system/runlevel2.target -> multi-user.target
lrwxrwxrwx. 1 root root 17 9月 13 17:03 /usr/lib/systemd/system/runlevel3.target -> multi-user.target
lrwxrwxrwx. 1 root root 17 9月 13 17:03 /usr/lib/systemd/system/runlevel4.target -> multi-user.target
lrwxrwxrwx. 1 root root 16 9月 13 17:03 /usr/lib/systemd/system/runlevel5.target -> graphical.target
lrwxrwxrwx. 1 root root 13 9月 13 17:03 /usr/lib/systemd/system/runlevel6.target -> reboot.target
現在のtargetの確認する。
# systemctl get-default
graphical.target
現在のgraphical.target(runlevel5)をmulti-user.targetのCLI ( runlevel=3 )ログインに変更する
# systemctl set-default multi-user.target
Removed /etc/systemd/system/default.target.
Created symlink /etc/systemd/system/default.target → /usr/lib/systemd/system/multi-user.target.
現在のtargetが、multi-user.targetに変更になったことを確認
# systemctl get-default
multi-user.target
メモリ上に常駐しているsystemdのデーモンを確認
# pstree -p | grep systemd
systemd(1)-+-ModemManager(893)-+-{ModemManager}(907)
|-systemd(2132)-+-(sd-pam)(2136)
|-systemd-journal(598)
|-systemd-logind(875)
|-systemd-machine(774)
|-systemd-udevd(640)
systemdがまず最初にみるdefault.targetの設定ファイルを確認すると、シンボリックリンクになっています。
# ls -l /etc/systemd/system/default.target
lrwxrwxrwx. 1 root root 40 10月 19 00:57 /etc/systemd/system/default.target -> /usr/lib/systemd/system/graphical.target
つまり、Linuxをデフォルトで、graphical.target(GUI)操作できるようになっているのがわかる。
設定ファイルを追って依存・順序関係を把握する(教科書100回読むより絶対この方が理解できる)
# cat -n /etc/systemd/system/default.target
10 [Unit]
11 Description=Graphical Interface
12 Documentation=man:systemd.special(7)
13 Requires=multi-user.target
14 Wants=display-manager.service
15 Conflicts=rescue.service rescue.target
16 After=multi-user.target rescue.service rescue.target display-manager.service
17 AllowIsolate=yes
systemdは、multi-user.targetを前提とするunitを探してそのunitを起動する(13行目)。
設定ファイルmulti-user.target内に記述された前提を見る
# cat -n /usr/lib/systemd/system/multi-user.target
10 [Unit]
11 Description=Multi-User System
12 Documentation=man:systemd.special(7)
13 Requires=basic.target
14 Conflicts=rescue.service rescue.target
15 After=basic.target rescue.service rescue.target
16 AllowIsolate=yes
13行目、Requiresの設定ファイルbasic.target内に記述された前提を見る
# nl /usr/lib/systemd/system/basic.target
9 [Unit]
10 Description=Basic System
11 Documentation=man:systemd.special(7)
12 Requires=sysinit.target
13 Wants=sockets.target timers.target paths.target slices.target
14 After=sysinit.target sockets.target paths.target slices.target tmp.mount
12行目、Requiresの設定ファイルsysinit.target内に記述された前提を見る
# nl /usr/lib/systemd/system/sysinit.target
9 [Unit]
10 Description=System Initialization
11 Documentation=man:systemd.special(7)
12 Conflicts=emergency.service emergency.target
13 Wants=local-fs.target swap.target
14 After=local-fs.target swap.target emergency.service emergency.target
13行目、wantsの設定(=可能な限り同時設定)local-fs.target swap.target
以上のことからsystemdは、以下の順で各unitを把握していることがわかる。
default.target >> multi-user.target >> basic.target >> sysinit.target
依存関係を確認するために、一つ一つ設定ファイルを追っていくのは骨が折れる。これを"systemctl list-dependencies" コマンドにより、systemdが認識する依存関係を表示できる
# systemctl list-dependencies default.target | grep target
default.target
● └─multi-user.target
● ├─basic.target
● │ ├─paths.target
● │ ├─slices.target
● │ ├─sockets.target
● │ ├─sysinit.target
● │ │ ├─cryptsetup.target
● │ │ ├─local-fs.target
● │ │ └─swap.target
● │ └─timers.target
● ├─getty.target
● ├─nfs-client.target
● │ └─remote-fs-pre.target
● └─remote-fs.target
● └─nfs-client.target
● └─remote-fs-pre.target