Plamo Linuxの特徴的な部分を紹介しているこのシリーズ、今回はカーネルと共に読み込まれ、カーネルの起動を補助するinitramfsを取り上げます。initramfsは、かつてはinitrdと呼ばれ、Linuxの初期のころから利用されてきた機能です。このinitrd/
initramfsの歴史
initrd/
Linuxが参考にしたUnixワークステーションは、SunやDEC、HPといったコンピュータメーカがハードウェアからOSまで一貫して作っていたので、対応すべき周辺機器も限られ、必要となる全てのドライバをあらかじめカーネルに組み込んでおくことが可能でした。
一方PC互換機では、技術的な仕様が公開、共有されたため、多数の周辺機器メーカが参入し、膨大な種類の周辺機器が開発されました。それら周辺機器メーカはDOSやWindows用のドライバは提供するものの、Linuxなど眼中にありませんでしたので、最初期のカーネルでは、ドライバを開発し対応する周辺機器を増やすことが最重要課題でした。
その後、Linuxの知名度があがるにつれ対応する周辺機器は増えてきたものの、今度はそれら周辺機器用のドライバが増えすぎて、全てをカーネルに組み込むことが困難になってきました。そこで考案されたのが、ドライバ類をモジュールとして分離し、必要に応じて組み込むモジュール化カーネルというアイデアです。
全ての機能やドライバを1つにまとめる伝統的な設計を
モジュール化カーネル以前では
もっとも、これらモジュール化されたドライバはHDD上に置くため、HDD用のドライバをモジュール化すると
initrdの元々の実装ではramdiskを仮想的なルートパーティションにしていたため、ファイルをファイルシステムとしてマウントするループバック・
その後Linuxの進化と共に、メモリ上に仮想的なファイルシステムを作るramfsが開発され、サイズが固定されるramdiskよりも柔軟な使い方が可能となり、それに合わせてinitrdもinitramfsに進化し、ファイルシステム・
現在ではこのinitramfsが標準になっているものの、機能の名称としては"initrd"も使われており、grubのメニュー等では"initrd=.."で指定するままだし、Plamo Linuxでもファイル名は"initrd.
initramfsの展開
さて、それではinitramfsの中身を見てみましょう。Plamo Linuxでは/bootディレクトリにカーネルイメージとそれに対応したinitramfsを収めています。
$ ls /boot System.map@ initrd.img-6.1.42-plamo64* System.map-6.1.42-plamo64* initrd.img-6.1.44-plamo64* System.map-6.1.44-plamo64* initrd.img-6.1.47-plamo64* System.map-6.1.47-plamo64* vmlinuz@ config-6.1.42-plamo64* vmlinuz-6.1.42-plamo64* config-6.1.44-plamo64* vmlinuz-6.1.44-plamo64* config-6.1.47-plamo64* vmlinuz-6.1.47-plamo64* efi/
initramfsに含まれるモジュールはカーネルのバージョンに依存するため、カーネルをバージョンアップすればinitramfsもそれに合わせて更新する必要があります。
さて、上記initrd.
$ file /boot/initrd.img-6.1.47-plamo64 /boot/initrd.img-6.1.47-plamo64: ASCII cpio archive (SVR4 with no CRC)
cpio形式のアーカイブファイルの中身を調べるには"cpio -t"コマンドを用います。cpioは入力元に標準入力を想定するので、ファイルを読ませる場合は"<"
$ cpio -t < /boot/initrd.img-6.1.47-plamo64 . kernel kernel/x86 kernel/x86/microcode kernel/x86/microcode/GenuineIntel.bin kernel/x86/microcode/AuthenticAMD.bin 25663 ブロック
おや、コマンドやモジュール類は見当りません。というのもPlamo LinuxのinitramfsにはCPU用のmicrocodeを先頭に付加しているため、そのままでは先頭のmicrocode分しか表示されないのです。
CPU用のmicrocodeはCPUのハードウェアレベルのバグ
そのためinitramfsの実際の中身を見るには、先頭からmicrocode分の25663ブロックを飛ばして読む必要があります。一定数のブロックを読み飛ばすにはddコマンドの"skip"オプションが使えます。また、このアーカイブファイルはgzipで圧縮しているので、読み出すまえには展開する必要があり、アーカイブからファイルを取り出すにはcpioの"-i"オプションを指定します。
$ dd if=/boot/initrd.img-6.1.47-plamo64 skip=25663 | gunzip | cpio -iv . init lib64 etc etc/lvm etc/lvm/lvmlocal.conf etc/lvm/lvm.conf etc/lvm/profile ... lib lib/modules lib/modules/6.1.47-plamo64 lib/modules/6.1.47-plamo64/modules.devname lib/modules/6.1.47-plamo64/modules.builtin.alias.bin lib/modules/6.1.47-plamo64/modules.builtin.bin ... 28745294 bytes (29 MB, 27 MiB) copied, 0.69324 s, 41.5 MB/s 94062 ブロック $ ls -F bin/ dev/ etc/ init* lib/ lib64@ proc/ run/ sbin/ sys/ usr/
ここに展開されたディレクトリやファイルがinitramfsの本体です。カーネルは、ブートローダによって共に読み込まれたinitramfsのアーカイブファイル
initramfsの構造
initramfsの役割はカーネルが本来のルートファイルシステムを読み込むために必要なモジュールドライバを提供することで、そのためのコマンドやライブラリ、必要なモジュール等を用意しています。
最近のLinuxには膨大な数のモジュールドライバが提供されているものの、initramfsに取り込んでいるのはHDDやメモリカード、USBメモリといったルートパーティションになりうる各種ブロックデバイス用のドライバと各種ファイルシステム、ファイルシステムが暗号化されていた場合に備えてのcrypto回りのモジュールのみです。
$ ls -F lib/modules/6.1.47-plamo64/kernel/ crypto/ drivers/ fs/ lib/ $ ls -F lib/modules/6.1.47-plamo64/kernel/drivers/ ata/ block/ firewire/ md/ message/ mmc/ mtd/ pcmcia/ scsi/ usb/ virtio/ $ ls -F lib/modules/6.1.47-plamo64/kernel/drivers/scsi/ 3w-9xxx.ko.zst fdomain_pci.ko.zst pmcraid.ko.zst 3w-sas.ko.zst fnic/ qedf/ 3w-xxxx.ko.zst hpsa.ko.zst qedi/ BusLogic.ko.zst hptiop.ko.zst qla1280.ko.zst ... $ ls -F lib/modules/6.1.47-plamo64/kernel/fs/ btrfs/ erofs/ fuse/ lockd/ nls/ reiserfs/ unicode/ cachefiles/ exfat/ gfs2/ mbcache.ko.zst ntfs3/ romfs/ vboxsf/ ceph/ ext2/ hfsplus/ netfs/ ocfs2/ smb/ xfs/ ...
bin/以下にはモジュールの組み込みとファイルシステムをマウントするために最低限必要なツールを用意しています。
$ ls -F bin/ basename* cp* insmod@ kmod* ls* mkdir* mount* rm* sh* umount* cat* dd* killall* ln* lsmod@ mknod* readlink* sed* sleep* uname*
sbin/以下は周辺機器を認識するためのudevdと、現状のインストーラでは対応していないものの、ルートパーティションを論理ボリュームやRAID上に置いた際に必要となるコマンド類です。
$ ls sbin/ blkid* lvdisplay@ lvscan@ pvchange@ pvscan@ vgchange@ vgscan@ dmsetup* lvextend@ mdadm* pvck@ switch_root* vgck@ lvchange@ lvm* mdmon* pvcreate@ udevadm* vgcreate@ lvcreate@ lvrename@ modprobe* pvdisplay@ udevd* vgrename@
lib/以下には上記バイナリファイルを動かすのに必要な共有ライブラリのみを置いています。
$ ls -F lib firmware/ libcap.so.2* libmount.so.1* libz.so.1* ld-linux-x86-64.so.2* libdevmapper-event.so.1.02* libncursesw.so.6* libzstd.so.1* libacl.so.1* libdevmapper.so.1.02* libpthread.so.0* modules/ libaio.so.1* libdl.so.2* libreadline.so.8* udev/ libattr.so.1* libkmod.so.2* librt.so.1* libblkid.so.1* liblzma.so.5* libtinfow.so.6* libc.so.6* libm.so.6* libudev.so.1*
これらを利用して必要なモジュールを組み込むのがinitの仕事です。initramfs用のinitは100行ほどのシェルスクリプトで、udevdを起動してカーネルに周辺機器を認識させ、あらかじめ用意しているモジュールを組み込ませることが主な仕事です。
処理を簡単に紹介すると、まずカーネルの動作に必要な/sysや/procといった仮想ファイルシステムをマウントし、
65 mount -n -t devtmpfs devtmpfs /dev
66 mount -n -t proc proc /proc
67 mount -n -t sysfs sysfs /sys
68 mount -n -t tmpfs tmpfs /run
grubから渡された起動用パラメータを/proc/
70 read -r cmdline < /proc/cmdline
71
72 for param in $cmdline ; do
73 case $param in
74 init=* ) init=${param#init=} ;;
75 root=* ) root=${param#root=} ;;
76 rootdelay=* ) rootdelay=${param#rootdelay=} ;;
77 rootfstype=*) rootfstype=${param#rootfstype=} ;;
78 rootflags=* ) rootflags=${param#rootflags=} ;;
79 ro ) ro="ro" ;;
80 rw ) ro="rw" ;;
81 esac
82 done
udevdを起動し、"udevadm trigger"で接続されている周辺機器をカーネルに認識させ、initramfs上に用意しているブロックデバイスやファイルシステムのモジュールを読み込ませます。
96 ${UDEVD} --daemon --resolve-names=never
97 udevadm trigger --action=add --type=subsystems
98 udevadm trigger --action=add --type=devices
99 udevadm trigger --action=change --type=devices
100 udevadm settle
正しく処理が進めばこの段階からHDD等が利用できるので、起動用パラメータを元に"do_
106 do_mount_root
107
108 killall -w ${UDEVD##*/}
最後に"switch_
110 exec switch_root /.root "$init" "$@"
以後の作業は本来のルートパーティション上にある/sbin/
/sbin/mkinitramfs
initramfsを作成するには/sbin/
$ sudo /sbin/mkinitramfs 6.1.47-plamo64 [sudo] kojima のパスワード: Creating initrd.img-6.1.47-plamo64... depmod: WARNING: could not open .. . ./kernel ./kernel/x86 ./kernel/x86/microcode ./kernel/x86/microcode/GenuineIntel.bin ./kernel/x86/microcode/AuthenticAMD.bin 25663 ブロック done. $ ls -lh 合計 53M -rw-r--r-- 1 root root 41M 10月 16日 09:03 initrd.img-6.1.47-plamo64 -rw-r--r-- 1 root root 13M 10月 16日 09:03 ucode.cpio
mkinitramfsの実行時にdepmodのWARNINGが表示されているのは気にしないでください
こうして作成した"initrd.
# grub-mkconfig -o grub.cfg.new Generating grub configuration file ... Found background: /usr/share/grub/plamo_bg01.png Linux イメージを見つけました: /boot/vmlinuz-6.1.47-plamo64 Found initrd image: /boot/initrd.img-6.1.47-plamo64 fgrep: warning: fgrep is obsolescent; using grep -F Linux イメージを見つけました: /boot/vmlinuz-6.1.44-plamo64 ... Check GRUB_DISABLE_OS_PROBER documentation entry. Adding boot menu entry for UEFI Firmware Settings ... 完了
initramfをどのような作りにするかはそれぞれのディストリビューションに委ねられています。そのためPlamo Linuxでは、
今回紹介した起動用のinitramfsは使い捨ての簡単な作りでしたが、必要なコマンドやライブラリ類を用意すればinitramfs上で動き、HDD等を必要としないLinux環境を作ることもできます。次回はそのような使い方の例としてPlamo Linuxのインストーラを取りあげる予定です。