【Linux】Systemd - Unitの概念を理解する

## systemdとは

systemdはカーネルが生成する最初のユーザプロセスで、Linuxの起動処理やシステム全体の管理を行います。
プロセス番号`PID=1で`、デーモン（メモリー上に常に待機している常駐プログラム）です。

要するに、パソコンの電源ボタン押してからシステムが起動するまでの間で誰よりも先人に立って
仕事をこなしていくリーダー的な存在です。

GRUB2によってHDDからメモリーにロードされたカーネルは、H/Wを認識できるようになった後、
PID=1のsystemdを起動する。
GRUB2はこちらの記事参照→作成中

【システム起動の流れ】

![1665669286960-ImYQgj.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935967/a3860152-83dd-ec3d-ac41-6c5c542e65ed.png)


![1666018594633-4b6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935967/90f0590e-9076-0d6b-b1d1-0823164e3de9.png)

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に置き換わる。

![noteに使う画像作成スライド.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935967/96502e14-9b1e-19a8-95ba-10d451fb44ea.jpeg)

systemdはinit以前の起動プロセスに影響しない。
図だけ見るとあまり変わっていないように見えるかもしれないが、
これは全く違う仕組みで起動処理にかかる速度も大幅に改善した。

今回は、systemdの動作の基礎となる【Unit】の概念を焦点を当てる。

### initのデメリット

従来のinitの起動プロセスは、スクリプトで上から順番に直線的に起動していくので、
上流の起動で手間とると、以降のすべての実行するサービスが待たされてしまい時間がかかる。

![initの起動プロセス.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935967/ca005e20-41f1-3e2c-2412-374e54addbbf.jpeg)

### systemdのメリット

従来のスクリプトを廃止し、【Unit】という単位で処理を管理する。
これまで/etc/rc.d/配下のスクリプトが実施していた処理の内容は、
全てUnitとして定義され直接systemdがunitを起動する。以下のプロセスを見ても、
initに比べて直線的ではなく、並列に起動しているのがわかる。

![systemdの起動プロセス.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935967/939dd25b-f781-1b9f-9005-ee5e48b7aaf7.jpeg)

### Unitの特徴

①スクリプトではなく設定ファイル
 →Unitの設定をもとにsystemd自身が処理を実行する
②Unit間の依存・順序関係（並列処理）の定義
 →「Aプロセス」を起動するには「Bプロセス」が必要など。

Unitは、「target」「mount」「service」「device」など、役割によってタイプがわかれていて、それぞれのunitは、依存関係が定義されている。
最初のプロセスPID=1としてsystemdが起動すると、「default.target」というUnitを頂点とする、依存関係のツリーを構築した後、依存するUnitを起動していく。

```:/default.target
$ 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依存関係の定義.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935967/b72f423d-13a8-98d0-2dec-16d945f5ffac.jpeg)

この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が存在

![e833b63447cfdf654488eda388680bf8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935967/509f0084-0206-17c6-a9cb-1d82882fb75c.png)


![538802.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935967/aeffa3fe-0e8c-ede5-4f3d-61ca4763110d.png)

これはシステム起動状態を表す従来のrunlevelとtargetの対応表であるが、コマンドでも確認できる。


```:/usr/lib/systemd/system/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
```