Linux の起動に必要なファイルを逆に辿る

これは Ubuntu 22.04 LTS が起動したところ。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/d533e4fc-7ca1-e154-7108-4e839f21ee17.png)

これが起動するまでに動いているプログラムをたどっていきます。

# 環境

- Ubuntu 22.04 LTS ja

# デスクトップ起動時に動作するプログラム

Ubuntu 22.04 ではデスクトップマネージャに Gnome が使われていて、gnome-session-properties で起動時のプログラムが指定できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/60b8ceef-eff1-ac58-4fea-d181ca5f597c.png)

しかしながら、SSH 鍵エージェントなんかよりももっと沢山、デスクトップの上で起動していますよね。
ここらへんとか
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/1d12d576-e513-022b-7d3f-99e40c29dedd.png)
ここらへんとか。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/7a3bce55-1d1c-726e-28b5-cbd2133e16c0.png)

日本語変換は Mozc ですし 左にあるメニューは Ubuntu Dock です。これらはどうやって呼び出されているのでしょう?

# Ubuntu Dock

これは gnome シェル拡張(extention)としてプレインストールされています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/ffdb9ee2-4d32-55f3-7331-b8cf2b67a9ea.png)

この extention の実態は
`/usr/share/gnome-shell/extensions/ubuntu-dock@ubuntu.com`

にあります。

これは Gnome-session が呼び出されたときに extention も呼び出されているのかな?



# gdm3 

これはデスクトップ画面が表示される前のログイン UI。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/8fac853e-fb40-bd98-b744-026f23fd96f6.png)

このログイン UI は gdm3 によって提供されています。gdm3 はサービスとして起動していて、ログオン後のサービスの状況を見てみると以下のようになっています。

```
$ sudo service gdm3 status
● gdm.service - GNOME Display Manager
     Loaded: loaded (/lib/systemd/system/gdm.service; static)
     Active: active (running) since Sun 2024-01-14 22:23:12 JST; 1 week 2 days ago
   Main PID: 1193 (gdm3)
      Tasks: 3 (limit: 76595)
     Memory: 6.9M
        CPU: 33ms
     CGroup: /system.slice/gdm.service
             └─1193 /usr/sbin/gdm3

 1月 14 22:23:12 nanbuwks-B550M-S2H systemd[1]: Starting GNOME Display Manager...
 1月 14 22:23:12 nanbuwks-B550M-S2H systemd[1]: Started GNOME Display Manager.
 1月 14 22:23:12 nanbuwks-B550M-S2H gdm-autologin][1230]: gkr-pam: no password is available for user
 1月 14 22:23:12 nanbuwks-B550M-S2H gdm-autologin][1230]: pam_unix(gdm-autologin:session): session opened for user nanbuwks(uid=1000) by (uid=0)
 1月 14 22:23:13 nanbuwks-B550M-S2H gdm-autologin][1230]: gkr-pam: gnome-keyring-daemon started properly

```
`pstree` で見ると、gdm3 から gnome-session が呼ばれているのがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/9fe06275-7057-8a6b-a7ec-a34dcc7a246c.png)

さて、gdm3 の設定は、`/etc/gdm3` ディレクトリにある各ファイルに記述されています。

例えば、`/etc/gdm3/Init/Default`  の一部は以下のようになっていて、ログオン後の処理が記述されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/4a9e4d76-7cdb-0eba-c59b-88ce4b22b468.png)

更に gdm3 から Weyland やデスクトップマネージャの呼び出しがなされていて、一例として Weyland に関する設定は `/etc/gdm3/custom.conf` 中に書かれています。

```
# Uncomment the line below to force the login screen to use Xorg
#WaylandEnable=false
```

# XDG

デスクトップ画面を作るための設定は、`/etc/gdm3` による設定以外にも多くの設定箇所があります。多くのデスクトップ環境では、XDG オートスタート設定を使うようになっています。h

XDG についての設定類は環境変数 XDG_CONFIG_DIRS で設定されています。

`XDG_CONFIG_DIRS=/etc/xdg/xdg-ubuntu-xorg:/etc/xdg`

さきほど、Mozc や Dash はどこから呼び出されるのかについて話題にしましたが、例えば mozc の起動設定などは以下のように設定されています。

```
$ cat /etc/xdg/autostart/ibus-mozc-launch-xwayland.desktop 
[Desktop Entry]
Name=Launch Xwayland on startup of wayland session
Comment=This ensures that ibus-x11, needed for ibus-mozc, is started
Type=Application
Exec=sh -c 'if [ "$XDG_SESSION_TYPE" = "wayland" ]; then xrefresh; fi'
TryExec=xrefresh
OnlyShowIn=GNOME;
NoDisplay=true
```

# `/etc/rc*.d/`

今まではログイン画面から後の処理を見ていました。さて、ログイン画面が出現する前は以下のスプラッシュが表示されますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/2403fa77-6c41-2aac-4615-2c3bd98c8776.png)

このスプラッシュは裏で動いている各種表示を隠しています。ESC キーでスプラッシュを消すと、起動中の詳細が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/9fff881d-eaa3-decf-da3b-3e3e47a5c8da.png)

この `[  OK  ]` と表示されているのは `/etc/rc*.d/` 中にある起動スクリプトの結果です。
例えば `/etc/rc5.d/S01gdm3` では、 gdm3 の起動が記述されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/474c48a2-2e65-7d6c-58af-7732ddfa5633.png)

 `/etc/rc*.d/` 内にあるスクリプトは、 systemd が呼び出しているようです。
更に実は先の`[  OK  ]`と表示しているのも systemd が介在しています。 systemd のソース `job.c` に以下のように記されています。

```
static const struct {
        int log_level;
        const char *color, *word;
} job_done_messages[_JOB_RESULT_MAX] = {
        [JOB_DONE]        = { LOG_INFO,    ANSI_OK_COLOR,         "  OK  " },
        [JOB_CANCELED]    = { LOG_INFO,                                    },
        [JOB_TIMEOUT]     = { LOG_ERR,     ANSI_HIGHLIGHT_RED,    " TIME " },
        [JOB_FAILED]      = { LOG_ERR,     ANSI_HIGHLIGHT_RED,    "FAILED" },
        [JOB_DEPENDENCY]  = { LOG_WARNING, ANSI_HIGHLIGHT_YELLOW, "DEPEND" },
        [JOB_SKIPPED]     = { LOG_NOTICE,  ANSI_HIGHLIGHT,        " INFO " },
        [JOB_INVALID]     = { LOG_INFO,                                    },
        [JOB_ASSERT]      = { LOG_WARNING, ANSI_HIGHLIGHT_YELLOW, "ASSERT" },
        [JOB_UNSUPPORTED] = { LOG_WARNING, ANSI_HIGHLIGHT_YELLOW, "UNSUPP" },
        [JOB_COLLECTED]   = { LOG_INFO,                                    },
        [JOB_ONCE]        = { LOG_ERR,     ANSI_HIGHLIGHT_RED,    " ONCE " },
};

```
# systemd
更に遡ると、`[  OK  ]` が出てこない起動表示が現れています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/8b6e087a-0f9a-c7d6-bfbb-0d6853e53a4d.png)

ここらへんは `/etc/systemd/system/` の中にある設定を実行しています。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/db52eaa8-e5f6-b497-663d-fcee4888fd26.png)

また、上の方にある udevd.service などは、/lib/systemd 内にある systemd-udevd などが呼び出されているみたいです。


# /sbin/init

systemd の実態は `lib/systemd/systemd` にあります。

ここらへんは /sbin/init から呼び出されます。
実は /sbin/init は systemd そのものです。
```
$ ls -alh init
lrwxrwxrwx 1 root root 20  8月 22 06:11 init -> /lib/systemd/systemd
```

init はプロセス番号が1番で動作して、一番最初に動作しているプログラムのように見えます。

```
$ ps auxww | grep init
root           1  0.0  0.0 168516 13008 ?        Ss    1月14   0:16 /sbin/init splash
```

しかしながら、最初と言っても /sbin/init は ルートファイルシステムがマウントされたあとに最初に呼び出されるものであるという条件がつきます。

これは果たしてどういったことなのでしょうか?

# /init

`/sbin/init` はどこから呼び出されるかと言うと Linux カーネルの最初のプロセスとして動作する `/init` です。

しかしながら `init`  というファイルは `/`  に存在しません。

```
$ ls /init
ls: '/init' にアクセスできません: そのようなファイルやディレクトリはありません
```

これはどういうことかというと、`/init` は別のファイルシステム中にあるためです。 `/init` が起動すると、ルートファイルシステムをマウントする処理を行います。 ルートファイルシステムがマウントされる前に`/init` 起動しておく必要があり、`/init` は初期に使用する別のファイルシステムに格納されています。


# grub

これは、/boot/grub/grub.cfg から Linux のブートの指定部分を抜き出したものです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/d78261c3-ed83-f14b-e9f6-e9dcae965501.png)


歴史上の理由でココらへんのファイルは /boot  に格納されています。

cf., https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard

ここの最後に initrd というのがありますね。これは、ルートファイルシステムが動作する前に、Linux カーネルを仮のファイルシステムで動かすものとして initrd または initramfs  がります。どちらでもいいのですが、 Ubuntu では initrd が使われているようです。

initrd は RAM ディスクのイメージであり、 initrd または initramfs  にアクセスするためのデバイスドライバはカーネルに静的にリンクされていて、RAMディスクとLinuxカーネルをメモリに展開し、Linux カーネルに RAM ディスクのアドレスを与えて起動すると Linux が動作し始めます。

# RAMディスクイメージ
  
  initrd 中にある RAM ディスクイメージを展開してみましょう。
  
  /boot 内にある `initrd.img-6.3.13-060313-generic` を使いました。
  
```
$ file initrd.img-6.3.13-060313-generic 
$ unzstd initrd.img-6.3.13-060313-generic -o initrd.img
```
 結構なサイズになりますね。

```
$ ls -alh
合計 260M
drwxrwxr-x   3 nanbuwks nanbuwks 4.0K  1月 24 22:41 .
drwxr-xr-x 259 nanbuwks nanbuwks 132K  1月 24 22:22 ..
-rw-r--r--   1 nanbuwks nanbuwks 214M  1月 24 22:22 initrd.img
-rw-r--r--   1 root     root      60M  1月 24 22:22 initrd.img-6.3.13-060313-generic
```
ファイルを抽出します。

```

$ mkdir initrdfiles
$ cd initrdfiles/
$ cat ../initrd.img | cpio -idv
```
中身は・・・

```
$ ls -alh
合計 40K
drwxrwxr-x 8 nanbuwks nanbuwks 4.0K  1月 24 22:42 .
drwxrwxr-x 3 nanbuwks nanbuwks 4.0K  1月 24 22:41 ..
lrwxrwxrwx 1 nanbuwks nanbuwks    7  1月 24 22:42 bin -> usr/bin
drwxr-xr-x 3 nanbuwks nanbuwks 4.0K  1月 24 22:42 conf
drwxr-xr-x 8 nanbuwks nanbuwks 4.0K  1月 24 22:42 etc
-rwxr-xr-x 1 nanbuwks nanbuwks 7.2K  1月 24 22:42 init
lrwxrwxrwx 1 nanbuwks nanbuwks    7  1月 24 22:42 lib -> usr/lib
lrwxrwxrwx 1 nanbuwks nanbuwks    9  1月 24 22:42 lib32 -> usr/lib32
lrwxrwxrwx 1 nanbuwks nanbuwks    9  1月 24 22:42 lib64 -> usr/lib64
lrwxrwxrwx 1 nanbuwks nanbuwks   10  1月 24 22:42 libx32 -> usr/libx32
drwxr-xr-x 2 nanbuwks nanbuwks 4.0K  1月 24 22:42 run
lrwxrwxrwx 1 nanbuwks nanbuwks    8  1月 24 22:42 sbin -> usr/sbin
drwxr-xr-x 7 nanbuwks nanbuwks 4.0K  1月 24 22:42 scripts
drwxr-xr-x 8 nanbuwks nanbuwks 4.0K  1月 24 22:42 usr
drwxr-xr-x 3 nanbuwks nanbuwks 4.0K  1月 24 22:42 var

```
`/usr/sbin` などもあって、シェルが使えることがわかります。

# /init の仕組み


https://github.com/torvalds/linux/blob/master/init/main.c
によると、以下のようにカーネルソースに /init がハードコーディングされていて、オプション設定でオーバーライドしない限りは カーネル立ち上がり時に /init を実行するようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/f75ce32a-e1af-a691-944f-910ffaa0df6d.png)

さて、先ほどRAMディスクの / にあった init ですが、
```

-rwxr-xr-x 1 nanbuwks nanbuwks 7.2K  1月 24 22:42 init
```
内部はシェルスクリプトになっていて、最後にはこのようになっています。
```
# Chain to real filesystem
# shellcheck disable=SC2086,SC2094
exec run-init ${drop_caps} "${rootmnt}" "${init}" "$@" <"${rootmnt}/dev/console" >"${rootmnt}/dev/console" 2>&1
echo "Something went badly wrong in the initramfs."
panic "Please file a bug on initramfs-tools."
```
そして、`${rootmnt}` や `${init}` の値として以下のように指定されています。
```
.
.
.
export init=/sbin/init
export readonly=y
export rootmnt=/root
.
.
.
```

run-init は、`${rootmnt}`  で与えられたファイルシステムを `/` としてマウントし直し、`${init}` で指定した /sbin/init プログラムをプロセス番号1番として動作させる働きがあります。

そしてここに来るまでに、`${rootmnt}`  で示された /root に、本来のルートファイルシステムが /root としてマウントされている状態になっています。

ということで、/sbin/init すなわち serviced が プロセス番号1番として起動し、サービスや /etc/rc* を実行、gdm3 で Weiland や gnome-session と共にログオンしてデスクトップが使えるようになります。