Linux

【PC】Linuxビルドのノウハウ ⑧/bootからどうやってブートファイルが読み出されるか、/lib/modules/からどうやってドライバが読み出されるか

【※ 当記事は2020年7月2日時点の情報です】

ペイヴメント(@pavement1234)です。

エンジニア
エンジニア

Linuxのカーネルビルドの方法を知りたいけど簡単にまとめられた情報源がない

こんな悩みを解決します。

今回やることは3つ。

  1. /bootからどうやってブートファイルが読み出されるか
  2. /lib/modules/からどうやってドライバが読み出されるか

バージョン情報

Linux Kernel 3.14

/boot

/bootの中にインストールされたinitrdとか、複数あるvmlinuzから3.14.79版がどう選ばれるのかを調べたいです。

①/bootにインストールされたイメージは、どうやって選ばれるのか?

Linuxがブートするまでを読みました。/boot/grub/menu.lstに起動するOSが書いてあるとありますが、私の環境にそんなファイルはありません。私のLinux(Ubuntu14 )とは環境が異なるようです。

システムの起動Linuxのブートシーケンスの基礎まとめを読みましたが、これも違う感じ。

8.11. カーネルのインストールを読みました。コレ当たりかも?

8.11.1. Debian カーネルパッケージの特徴
Debian カーネルパッケージはカーネルイメージ (vmlinuz-version) をインストールします。カーネルの設定 (config-version) とシンボルテーブル (System.map-version) は /boot/ に置かれます。シンボルテーブルは開発者がカーネルエラーメッセージの意味を理解する際の手助けになります。それどころかシンボルテーブルがなければ、カーネルの「oops」(「oops」とはカーネル空間で起こるユーザ空間プログラムのセグメンテーション違反に相当するエラーです。言い換えれば、不正なポインタを参照して値を取得したことで生成されるメッセージです) に含まれる情報は、数字で表したメモリアドレスだけになります。アドレスとシンボルや関数名を対応付けるテーブルがなければ、この情報は役に立ちません。モジュールは /lib/modules/version/ ディレクトリにインストールされます。
カーネルパッケージの設定スクリプトは自動的に initrd イメージを生成します。initrd はブートローダによってメモリに読み込まれる小さなシステムで (このため「init RAM ディスク」と名付けられています)、Linux カーネルは initrd を使って完全な Debian システムを含むデバイス (たとえば SATA ディスクのドライバ) にアクセスするために必要なモジュールを読み込みます。最後に、カーネルパッケージの post-installation スクリプトが /vmlinuz、/vmlinuz.old、/initrd.img、/initrd.img.old のシンボリックリンクを更新します。これらはそれぞれインストールされた最新の 2 つのカーネルとカーネルに対応する initrd イメージを指します。
これらの作業のほとんどは /etc/kernel/*.d/ ディレクトリの中にあるフックスクリプトが担っています。たとえば、grub との統合は、カーネルがインストールまたは削除された際に update-grub を呼び出す /etc/kernel/postinst.d/zz-update-grub と /etc/kernel/postrm.d/zz-update-grub が担っています。

カーネルインストール後、/etc/kernel/postinst.d/zz-update.grub、/etc/kernel/postrm.d/zz-update.grub、というスクリプトが/vmlinuz、/vmlinuz.old、/initrd.img、initrd.img.oldというシンボリックリンクを更新するとのこと。

早速、ルートディレクトリをls -lしてみましたが、リンクは古いままでした(3.13.0-24のまま)。残念ながらこれもハズレ。

$ ls -l /
合計 101
drwxr-xr-x 2 root root 4096 2月 17 2019 bin
drwxr-xr-x 5 root root 1024 8月 30 04:22 boot
drwxrwxr-x 2 root root 4096 2月 17 2019 cdrom
drwxr-xr-x 19 root root 4200 8月 27 23:03 dev
drwxr-xr-x 144 root root 12288 8月 27 23:03 etc
drwxr-xr-x 3 root root 4096 2月 17 2019 home
lrwxrwxrwx 1 root root 33 2月 17 2019 initrd.img -> boot/initrd.img-3.13.0-24-generic
lrwxrwxrwx 1 root root 33 2月 17 2019 initrd.img.old -> boot/initrd.img-3.13.0-24-generic
drwxr-xr-x 23 root root 4096 2月 17 2019 lib
drwxr-xr-x 2 root root 4096 2月 17 2019 lib32
drwxr-xr-x 2 root root 4096 2月 17 2019 lib64
drwxr-xr-x 2 root root 4096 2月 17 2019 libx32
drwx—— 2 root root 16384 2月 17 2019 lost+found
drwxr-xr-x 2 root root 4096 4月 19 2014 media
drwxr-xr-x 2 root root 4096 4月 11 2014 mnt
drwxr-xr-x 4 root root 4096 2月 24 2019 opt
dr-xr-xr-x 149 root root 0 8月 27 23:03 proc
drwx—— 2 root root 4096 2月 17 2019 root
drwxr-xr-x 24 root root 820 8月 30 04:12 run
drwxr-xr-x 2 root root 12288 2月 17 2019 sbin
drwxr-xr-x 2 root root 4096 4月 19 2014 srv
dr-xr-xr-x 13 root root 0 8月 30 04:23 sys
drwxrwxrwt 4 root root 4096 8月 30 04:17 tmp
drwxr-xr-x 12 root root 4096 2月 17 2019 usr
drwxr-xr-x 13 root root 4096 4月 22 2014 var
lrwxrwxrwx 1 root root 30 2月 17 2019 vmlinuz -> boot/vmlinuz-3.13.0-24-generic

よくわかんないので、/bootの下をpavement1234でgrepしてみることに。まずは1階層目(/boot/*)をgrepしたところ、大した情報得られず。

$ sudo grep pavement1234 /boot/*
grep: /boot/efi: ディレクトリです
grep: /boot/grub: ディレクトリです
grep: /boot/lost+found: ディレクトリです
バイナリファイル /boot/vmlinuz-3.14.79-pavement1234 に一致しました

次に2階層目(/boot/*/*)をチェック。/boot/glub/grub.cfgにpavement1234の記載が沢山みつかりました。私がちゃんとチェックできてなかっただけですね。

$ sudo grep pavement1234 /boot/*/*
grep: /boot/efi/EFI: ディレクトリです
grep: /boot/grub/fonts: ディレクトリです
/boot/grub/grub.cfg: linux /vmlinuz-3.14.79-pavement1234 root=/dev/mapper/ubuntu–vg-root ro quiet splash $vt_handoff
/boot/grub/grub.cfg: initrd /initrd.img-3.14.79-pavement1234
/boot/grub/grub.cfg: menuentry ‘Ubuntu, with Linux 3.14.79-pavement1234’ –class ubuntu –class gnu-linux –class gnu –class os $menuentry_id_option ‘gnulinux-3.14.79-pavement1234-advanced-e949b89d-bd34-49f2-8e9e-742e7ff1d2df’ {
/boot/grub/grub.cfg: echo ‘Linux 3.14.79-pavement1234 をロード中…’
/boot/grub/grub.cfg: linux /vmlinuz-3.14.79-pavement1234 root=/dev/mapper/ubuntu–vg-root ro quiet splash $vt_handoff
/boot/grub/grub.cfg: initrd /initrd.img-3.14.79-pavement1234
/boot/grub/grub.cfg: menuentry ‘Ubuntu, with Linux 3.14.79-pavement1234 (recovery mode)’ –class ubuntu –class gnu-linux –class gnu –class os $menuentry_id_option ‘gnulinux-3.14.79-pavement1234-recovery-e949b89d-bd34-49f2-8e9e-742e7ff1d2df’ {
/boot/grub/grub.cfg: echo ‘Linux 3.14.79-pavement1234 をロード中…’
/boot/grub/grub.cfg: linux /vmlinuz-3.14.79-pavement1234 root=/dev/mapper/ubuntu–vg-root ro recovery nomodeset
/boot/grub/grub.cfg: initrd /initrd.img-3.14.79-pavement1234
grep: /boot/grub/locale: ディレクトリです
grep: /boot/grub/x86_64-efi: ディレクトリです

/boot/grub/grub.cfgを開いてみました。DO NOT EDIT THIS FILEと書いてあります。grub-mkconfigが自動生成するからいじるなと。vmlinuz-3.14.79-pavement1234、initrd.img-3.14.79-pavement1234を指しているエントリーが今回インストールしたカーネルです。他に3.13.0-165、3.13.0-24のエントリーがありました。

$ vi /boot/grub/grub.cfg

最後に/boot/grub/grub.cfgの先頭に書いてあったgrub-mkconfigについて調べてみる

上にも書きましたが、grub-mkconfigが/boot/grub/grub.cfgを自動生成するようです。

$ vi /boot/grub/grub.cfg
#
# DO NOT EDIT THIS FILE
#
# It is automatically generated by grub-mkconfig using templates
# from /etc/grub.d and settings from /etc/default/grub
#

今回のお題 – GRUB を設定する ~ GRUB 2 編を読みました。詳細は割愛しますがザックリ言うとこういうことらしい。

GRUB 2 設定ファイルは/boot/grub/grub.cfgだが、このファイルを直接編集するのではなく/etc/default/grubと/etc/grub.d/にあるファイルに設定を書き、grub-mkconfigに-oオプションを指定して/boot/grub/grug.cfgを出力する。

/etc/default/grub、/etc/grub.d/*をくまなく調べましたが、当然ながらpavement1234は直接書かれてません。恐らくインストール時に一時的にシェル変数にxxx-pavement1234が設定されgrub-mkconfigが実行されるのだと思います。

$ ls -l /etc/grub.d
合計 76
-rwxr-xr-x 1 root root 9791 1月 17 2019 00_header
-rwxr-xr-x 1 root root 6058 4月 11 2014 05_debian_theme
-rwxr-xr-x 1 root root 11608 4月 11 2014 10_linux
-rwxr-xr-x 1 root root 10412 4月 11 2014 20_linux_xen
-rwxr-xr-x 1 root root 1992 3月 12 2014 20_memtest86+
-rwxr-xr-x 1 root root 11692 4月 11 2014 30_os-prober
-rwxr-xr-x 1 root root 1418 1月 17 2019 30_uefi-firmware
-rwxr-xr-x 1 root root 214 4月 11 2014 40_custom
-rwxr-xr-x 1 root root 216 4月 11 2014 41_custom
-rw-r–r– 1 root root 483 4月 11 2014 README

/lib/modules/3.14.79-pavement1234/

ドライバのロード順を制御するmodule.orderを調べたいです。

①何はともあれ/lib/modules/3.14.79-pavement1234に行ってみる

/lib/modules/3.14.79-pavement1234/をls -lしてみました。なんかいっぱいあるな…。

$ ls -l /lib/modules/3.14.79-pavement1234/
合計 3832
drwxr-xr-x 10 root root 4096 7月 9 18:22 kernel
-rw-r–r– 1 root root 947199 7月 9 16:48 modules.alias
-rw-r–r– 1 root root 930707 7月 9 16:48 modules.alias.bin
-rw-r–r– 1 root root 7004 7月 9 16:36 modules.builtin
-rw-r–r– 1 root root 9154 7月 9 16:48 modules.builtin.bin
-rw-r–r– 1 root root 372523 7月 9 16:48 modules.dep
-rw-r–r– 1 root root 548545 7月 9 16:48 modules.dep.bin
-rw-r–r– 1 root root 241 7月 9 16:48 modules.devname
-rw-r–r– 1 root root 153217 7月 9 16:36 modules.order
-rw-r–r– 1 root root 160 7月 9 16:48 modules.softdep
-rw-r–r– 1 root root 412275 7月 9 16:48 modules.symbols
-rw-r–r– 1 root root 515568 7月 9 16:48 modules.symbols.bin

カーネルモジュールの作り方を読みました。特にmodules.orderについて触れていませんが、カーネルモジュールをビルドするMakefileが載ってました。uname -rでカーネルのリリース番号を出力してKDIRを動的生成。私のPCでuname -rを打つと3.14.79-pavement1234と出力されます。これは便利。

Makefile

obj-m := test.o
KDIR := /lib/modules/$(shell uname -r)/build
VERBOSE = 0

all:
$(MAKE) -C $(KDIR) M=$(PWD) KBUILD_VERBOSE=$(VERBOSE) CONFIG_DEBUG_INFO=y modules
clean:
rm -f *.o *.ko *.mod.c Module.symvers modules.order

とりあえず馴染みがある/lib/modules/3.14.79-pavement1234配下のath10をgrepしてみました。module.alias、modules.dep、modules.order、modules.symbolsがヒット。

$ grep ath10 /lib/modules/3.14.79-pavement1234/*
grep: /lib/modules/3.14.79-pavement1234/kernel: ディレクトリです
/lib/modules/3.14.79-pavement1234/modules.alias:alias pci:v0000168Cd0000003Csv*sd*bc*sc*i* ath10k_pci
バイナリファイル /lib/modules/3.14.79-pavement1234/modules.alias.bin に一致しました
/lib/modules/3.14.79-pavement1234/modules.dep:kernel/drivers/net/wireless/ath/ath10k/ath10k_core.ko: kernel/drivers/net/wireless/ath/ath.ko kernel/net/mac80211/mac80211.ko kernel/net/wireless/cfg80211.ko
/lib/modules/3.14.79-pavement1234/modules.dep:kernel/drivers/net/wireless/ath/ath10k/ath10k_pci.ko: kernel/drivers/net/wireless/ath/ath10k/ath10k_core.ko kernel/drivers/net/wireless/ath/ath.ko kernel/net/mac80211/mac80211.ko kernel/net/wireless/cfg80211.ko
バイナリファイル /lib/modules/3.14.79-pavement1234/modules.dep.bin に一致しました
/lib/modules/3.14.79-pavement1234/modules.order:kernel/drivers/net/wireless/ath/ath10k/ath10k_core.ko
/lib/modules/3.14.79-pavement1234/modules.order:kernel/drivers/net/wireless/ath/ath10k/ath10k_pci.ko
/lib/modules/3.14.79-pavement1234/modules.symbols:alias symbol:ath10k_warn ath10k_core
/lib/modules/3.14.79-pavement1234/modules.symbols:alias symbol:ath10k_core_start ath10k_core
/lib/modules/3.14.79-pavement1234/modules.symbols:alias symbol:ath10k_core_create ath10k_core
/lib/modules/3.14.79-pavement1234/modules.symbols:alias symbol:ath10k_core_register ath10k_core
/lib/modules/3.14.79-pavement1234/modules.symbols:alias symbol:ath10k_core_destroy ath10k_core
/lib/modules/3.14.79-pavement1234/modules.symbols:alias symbol:ath10k_info ath10k_core
/lib/modules/3.14.79-pavement1234/modules.symbols:alias symbol:ath10k_core_stop ath10k_core
/lib/modules/3.14.79-pavement1234/modules.symbols:alias symbol:ath10k_core_unregister ath10k_core
/lib/modules/3.14.79-pavement1234/modules.symbols:alias symbol:ath10k_err ath10k_core
バイナリファイル /lib/modules/3.14.79-pavement1234/modules.symbols.bin に一致しました

modules.depは依存関係が書いてあるみたいです。(viで: set nuして)行数を出しました。

ath10k_pci.ko
+ath10k_core.ko
+ath.ko
+cfg80211.ko
+mac80211.ko
+cfg80211.ko

1047 kernel/drivers/net/wireless/ath/ath10k/ath10k_core.ko:: kernel/drivers/net/wireless/ath/ath.ko kernel/net/mac80211/mac80211.ko kernel/net/wireless/cfg80211.ko

1048 kernel/drivers/net/wireless/ath/ath10k/ath10k_pci.ko: kernel/drivers/net/wireless/ath/ath10k/ath10k_core.ko kernel/drivers/net/wireless/ath/ath.ko kernel/net/mac80211/mac80211.ko kernel/net/wireless/cfg80211.ko

1050 kernel/drivers/net/wireless/ath/ath.ko: kernel/net/wireless/cfg80211.ko

3785 kernel/net/wireless/cfg80211.ko:

3878 kernel/net/mac80211/mac80211.ko: kernel/net/wireless/cfg80211.ko

modules.orderでこれらがどういう位置関係になっているか見た。依存関係の下層(今回で言うとcfg802.ko)が先に呼ばれるとかではなくて、アルファベット順のように見える。

1048 kernel/drivers/net/wireless/ath/ath10k/ath10k_core.ko
1049 kernel/drivers/net/wireless/ath/ath10k/ath10k_pci.ko

1051 kernel/drivers/net/wireless/ath/ath.ko

3786 kernel/net/wireless/cfg80211.ko

3879 kernel/net/mac80211/mac80211.ko

lsmodで実際にロードされているモジュールをリスト表示でき、依存関係もわかります。私のLinux PCに搭載された無線LANモジュールはIntelなのでiwlwifiが呼ばれてます。

$ lsmod
Module Size Used by
btrfs 880060 0
raid6_pq 97812 1 btrfs
xor 21411 1 btrfs
autofs4 38754 0
ctr 13049 1
ccm 17773 1
arc4 12608 2
rfcomm 69064 8
iwlmvm 194784 0
bnep 19624 2
mac80211 656781 1 iwlmvm
nls_iso8859_1 12713 1
snd_hda_codec_hdmi 46480 1
snd_hda_codec_realtek 65984 1
snd_hda_codec_generic 68348 1 snd_hda_codec_realtek
uvcvideo 80834 0
videobuf2_vmalloc 13216 1 uvcvideo
videobuf2_memops 13362 1 videobuf2_vmalloc
videobuf2_core 41043 1 uvcvideo
videodev 148362 2 uvcvideo,videobuf2_core
keucr 71407 0
hid_multitouch 17407 0
intel_rapl 18773 0
coretemp 13475 0
kvm_intel 143079 0
kvm 463071 1 kvm_intel
iwlwifi 177968 1 iwlmvm
crct10dif_pclmul 14289 0
crc32_pclmul 13113 0
cryptd 20359 0
btusb 28339 0
bluetooth 406881 22 bnep,btusb,rfcomm
joydev 17381 0
6lowpan_iphc 18702 1 bluetooth
serio_raw 13462 0
snd_hda_intel 56531 3
snd_hda_codec 137502 4 snd_hda_codec_realtek,snd_hda_codec_hdmi,snd_hda_codec_generic,snd_hda_intel
snd_hwdep 13602 1 snd_hda_codec
cfg80211 519267 3 iwlwifi,mac80211,iwlmvm
snd_pcm 107781 3 snd_hda_codec_hdmi,snd_hda_codec,snd_hda_intel
snd_seq_midi 13324 0
snd_seq_midi_event 14899 1 snd_seq_midi
snd_rawmidi 30259 1 snd_seq_midi
sparse_keymap 13948 0
toshiba_bluetooth 12852 0
snd_seq 61616 2 snd_seq_midi_event,snd_seq_midi
i915 797368 2
i2c_hid 18661 0
drm_kms_helper 48716 1 i915
snd_seq_device 14497 3 snd_seq,snd_rawmidi,snd_seq_midi
video 19425 1 i915
parport_pc 32701 0
drm 298219 3 i915,drm_kms_helper
dw_dmac 12814 0
dw_dmac_core 24251 1 dw_dmac
mac_hid 13205 0
snd_timer 29481 2 snd_pcm,snd_seq
ppdev 17635 0
i2c_designware_platform 13006 0
i2c_designware_core 14768 1 i2c_designware_platform
snd 69545 18 snd_hda_codec_realtek,snd_hwdep,snd_timer,snd_hda_codec_hdmi,snd_pcm,snd_seq,snd_rawmidi,snd_hda_codec_generic,snd_hda_codec,snd_hda_intel,snd_seq_device,snd_seq_midi
i2c_algo_bit 13413 1 i915
8250_dw 13437 0
lp 17759 0
wmi 19177 0
shpchp 37032 0
soundcore 12680 1 snd
spi_pxa2xx_platform 23056 0
parport 42313 3 lp,ppdev,parport_pc
hid_generic 12548 0
usbhid 52567 0
hid 105788 4 i2c_hid,hid_multitouch,hid_generic,usbhid
usb_storage 66305 0
psmouse 106606 0
sdhci_acpi 13376 0
sdhci 43023 1 sdhci_acpi
r8169 71677 0
mii 13934 1 r8169
ahci 34014 3

そのうちLinux起動時にドライバがどうやって読み込まれるのかを調べてみようと思います(今回はやりません)。

まとめ

PC Linuxのカーネルを浅く広く弄り倒してみました。次は1つ1つ深堀していきたいです。

ABOUT ME
ペイヴメント
ペイヴメントのエンジニア塾(当ブログ)では20年以上の経験から得られたプログラミング系ノウハウについてベテランにも満足して頂けるような内容の濃いコンテンツを初心者にも分かりやすい形で日々発信しています。【経歴】ベンチャーのソフトハウスで4年勤務後、精密機器メーカーのソフト開発部門に勤務し今に至ります。