これは Ubuntu 22.04 LTS が起動したところ。
これが起動するまでに動いているプログラムをたどっていきます。
環境
- Ubuntu 22.04 LTS ja
デスクトップ起動時に動作するプログラム
Ubuntu 22.04 ではデスクトップマネージャに Gnome が使われていて、gnome-session-properties で起動時のプログラムが指定できます。
しかしながら、SSH 鍵エージェントなんかよりももっと沢山、デスクトップの上で起動していますよね。
ここらへんとか
ここらへんとか。
日本語変換は Mozc ですし 左にあるメニューは Ubuntu Dock です。これらはどうやって呼び出されているのでしょう?
Ubuntu Dock
これは gnome シェル拡張(extention)としてプレインストールされています。
この extention の実態は
/usr/share/gnome-shell/extensions/ubuntu-dock@ubuntu.com
にあります。
これは Gnome-session が呼び出されたときに extention も呼び出されているのかな?
gdm3
これはデスクトップ画面が表示される前のログイン UI。
このログイン 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 が呼ばれているのがわかります。
さて、gdm3 の設定は、/etc/gdm3
ディレクトリにある各ファイルに記述されています。
例えば、/etc/gdm3/Init/Default
の一部は以下のようになっていて、ログオン後の処理が記述されています。
更に 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/
今まではログイン画面から後の処理を見ていました。さて、ログイン画面が出現する前は以下のスプラッシュが表示されますね。
このスプラッシュは裏で動いている各種表示を隠しています。ESC キーでスプラッシュを消すと、起動中の詳細が表示されます。
この [ OK ]
と表示されているのは /etc/rc*.d/
中にある起動スクリプトの結果です。
例えば /etc/rc5.d/S01gdm3
では、 gdm3 の起動が記述されています。
/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 ]
が出てこない起動表示が現れています。
ここらへんは /etc/systemd/system/
の中にある設定を実行しています。
また、上の方にある 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 のブートの指定部分を抜き出したものです。
歴史上の理由でココらへんのファイルは /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 を実行するようです。
さて、先ほど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 と共にログオンしてデスクトップが使えるようになります。