[Linux] 起動時間をできるだけ短縮してみる

ノートパソコンでもなくただのデスクトップなので起動時間が長いのもしょうがないかなーとか思っていたのですがやっぱり起動時間が長いとモチベーションが下がるのでちょうど[Arch Linux](https://archlinux.org/)から[CachyOS](https://cachyos.org/)に移行したのを機にいろいろ変えてみました
# 環境
- マザボ：[MPG-Z590-GAMING-FORCE](https://jp.msi.com/Motherboard/MPG-Z590-GAMING-FORCE)
- CPU：[i5 11世代](https://www.intel.co.jp/content/www/jp/ja/products/sku/212271/intel-core-i511400f-processor-12m-cache-up-to-4-40-ghz/specifications.html)
- RAM：46GiB <sub>(49199524)</sub>
- OS：[CachyOS](https://cachyos.org/)

| デバイス名 | マウントポイント | 詳細 |
|:-:|:-:|:-:|
| `/dev/sda1` | `/boot` | `/dev/sda`はSSD |
| `/dev/sda2` | `/` |
| `/dev/sdb2` | `/run/media/hayattgd/HDD` | Windowsのときから使ってるデータ |
| `/dev/sdc2` | `/run/media/hayattgd/Arch` | 前のArch Linuxが入ってる場所 |
| `/dev/sdc3` | `/home` | 前のLinuxの`/home`と同じパーティション |

# 前提知識
- カーネル - ソフトウェア(initも含む)とハードウェアの中間にあるもの
- init - systemdなど、システム起動で呼び出されるプロセスやその仕組みのこと
# 目標
- ログイン画面にできるだけ早く到達する
- ssh, libvirtなど起動に関係ないものは起動時に使えなくても構わない（起動直後には使わないため）
- ただ、sshなどを使いたいときには使えるようにする
# 本題
まずはとりあえずChatGPTに聞いてみました
1. SSDにOSをいれる
2. 軽いLinux ディストリビューションを選ぶ
3. systemd-analyzeで起動時間のボトルネックを調べる
4. 不要なサービスを止める
5. ~~initをsystemd以外に変える (今更面倒くさいので却下)~~

ということを言われたのでその通りにしていきます
## SSDにOSを移す
これは難しいというか時間がかかっただけでした。
USBからGpartedを起動して、コピーしたり移動したりいろいろ...
もともとSSDにはWindowsが入っていたので外部のストレージに移しておきました。
一応前使ってたArch Linuxでも`~/.config`などの設定が変わらずに使えるように`/home`はHDDのパーティションをマウントしました。
`/`や`/boot`はすべてSSDにパーティションを置いて早く起動できるようにしました。
## 軽いLinux ディストロ
これはぶっちゃけインストール後に色々変えればどうとでもなる話ですが
- [CachyOS](https://cachyos.org/)（カーネルがカスタマイズされてる）
- [Void](https://voidlinux.org/)（systemdの代わりにrunit）
- [Alpine Linux](https://www.alpinelinux.org/)（systemdの代わりにopenrc）
- [Artix Linux](https://artixlinux.org/)（いろいろなinitから選べる）

などはインストール時にすでに最適化されています。
## systemd-analyzeで起動時間を調べる
```terminal
$ systemd-analyze
Startup finished in 17.469s (firmware) + 6.790s (loader) + 11.084s (kernel) + 7.852s (userspace) = 43.196s 
graphical.target reached after 4.802s in userspace.

$ systemd-analyze critical-chain
graphical.target @4.802s
└─multi-user.target @4.802s
  └─libvirtd.service @4.722s +79ms
    └─network.target @4.719s
      └─NetworkManager.service @4.535s +184ms
        └─basic.target @4.532s
          └─dbus-broker.service @4.499s +31ms
            └─dbus.socket @4.490s
              └─sysinit.target @4.484s
                └─systemd-update-utmp.service @4.453s +31ms
                  └─systemd-tmpfiles-setup.service @4.364s +87ms
                    └─local-fs.target @4.359s
                      └─run-media-hayattgd-HDD.mount @1.538s +2.821s
                        └─dev-sdb2.device @955ms
```
ファームウェア17秒 + ローダー7秒 + カーネル11秒 + `systemd`8秒 = 約43秒で起動していて、/dev/sdb2のマウントに3秒ほど時間をかけていることがわかります。
SSDでもないただのHDDでNTFSをマウントしようとしているのでそりゃ時間かかるよなぁ...
### userspace
userspaceはsystemdなどのinitのところで、ログイン画面までの時間です。
#### ディスクにアクセスされたときだけマウントする
ついでに前のArch Linuxが入っている/dev/sdc2も一緒に必要なとき(アクセスされたとき)だけマウントするようにします。
```sh:/etc/fstab
...
# /dev/sdc2
UUID=..   /run/media/hayattgd/Arch    ext4    noauto,x-systemd.automount,x-systemd.idle-timeout=600 ..

# /dev/sdb2 LABEL=HDD
UUID=..   /run/media/hayattgd/HDD    ntfs    noauto,x-systemd.automount,x-systemd.idle-timeout=600 ..
...
```
- `noauto` : 自動的にマウントしない
- `x-systemd.automount` : アクセスされたときにマウントする
- `x-systemd.idle-timeout` : どのくらいアクセスされなかったらアンマウントするか秒数指定

これで再起動してみたんですが...なんか勝手に/dev/sdb2がマウントされちゃう現象が発生
=> 起動するサービスの中に/dev/sdb2が必要なサービスがあった(libvirtdで/dev/sdb2のディスクを指定していた)のが原因

なので無効化しておきます。
```terminal
$ sudo systemctl disable libvirtd
```
#### サービスの遅延起動
このままだと目標からそれてしまう(libvirtdが簡単に使えない)ので、ログイン画面が出る前に行われる処理(default.target)より後に起動させます。(ついでにNetworkManagerも遅延起動)
```bash:/usr/local/bin/start-delayed-services.sh
#!/bin/bash
systemctl start libvirtd.service
systemctl start NetworkManager.service
```
```ini:/etc/systemd/system/start-delayed-services.service
[Unit]
Description=Start delayed services to speed up boot

[Service]
Type=oneshot
ExecStart=/usr/local/bin/start-delayed-services.sh
```
```ini:/etc/systemd/system/start-delayed-services.timer
[Unit]
Description=Actual timer file for delayed services

[Timer]
OnBootSec=30s
AccuracySec=1s

[Install]
WantedBy=timers.target
```
これで約３秒ほど起動時間が縮まりました！ちょうど/dev/sdb2のマウントにかかった時間と同じくらいです。

### kernel
kernelはinitが呼び出されるまでの時間です。
ここが10秒くらいだったので、`CachyOS Kernel Manager`というものを使って自分のCPU向けに最適化してコンパイルしました。コンパイルの時間はだいぶ長かったですが約６秒ほど起動時間が縮まりました！

### loader
loaderはGRUBやsystemd-boot、rEFIndなどでlinuxが呼び出されるまでの時間です。
いままでは`GRUB`を使っていたのですが、`systemd-boot`の方が早いということだったので変えてみました。
```terminal
$ sudo bootctl install
```
それでもともと約５秒だったものが３秒ほどになって、２秒縮まりました！
しかし`systemd-boot`だと背景がなくなって黒い背景に白い文字という起動時間に全振りしたようなものなのでそこは要検討ですね...

### firmware
firmwareはマザーボードがいろいろ起動処理をするのにかかる時間です。
ここは20秒とか結構大きい数値を叩き出してるのでどうにかしたいと思ったのですがやれることは少ないようです...
一応やったこととしては、
- マザーボードのBIOSにMSI Fast Bootという設定があったので有効化
- 使わないものをBIOSから無効化

# 結果
前：`17.469s (firmware) + 6.790s (loader) + 11.084s (kernel) + 7.852s (userspace) = 43.196s`
今：`21.716s (firmware) + 2.774s (loader) + 3.958s (kernel) + 1.857s (userspace) = 30.306s`

謎に`firmware`は増えていますがこれは結構起動するたびに変わるので、`firmware`を除外して
`6.790s (loader) + 11.084s (kernel) + 7.852s (userspace) = 25.726s`
`2.774s (loader) + 3.958s (kernel) + 1.857s (userspace) = 8.689s`
25.726 - 8.689 = 17.037

つまり...
- 約17秒短縮できた
- 合計起動時間：約28.689秒になった

# まとめ

1. 起動に関係するものは**SSDに移す**
2. systemd-analyzeで**遅い原因を調べる**
3. 起動に関係ないデータの**マウントはアクセス時だけ**にする
4. ネットワークマネージャなどの**サービスを遅延起動**にする
5. カーネルを**自分のCPU向けに最適化**する
6. systemd-bootなどの**軽いブートローダーを使う**
7. BIOSに**Fast Bootなどの項目があれば有効化**する

もう前の環境には戻れませんね