普段Linuxを使っていながら、vmlinuz
やinitrd.img
というファイルは何なのか、
あやふやにしか理解していなかったので、一通りLinuxマシンのブートの仕組みを
勉強してみた結果を書き留めておく。なお、BIOSとGRUB Legacyの環境を前提としている。
EFIやGRUB2を使った環境については、今後いずれ勉強していきたい。
基本的にOSの起動は、単純・低機能なプログラムが、より複雑・高機能なプログラムを 読み込み起動するという処理を連鎖的に行う仕組みになっている。 Linuxでは、下記のプログラムが順に起動していく:
- BIOS
- ブートローダ (GRUB)
- Stage 1
- Stage 1.5
- Stage 2
- Linuxカーネル
- Init
以下では、それぞれのプログラムについて順に要約して述べていく。
1. BIOS
現在一般的なx86/x86-64 CPUは、電源が投入されると、0xfffffff0
(Reset Vector)
番地から実行を開始する。この領域には、BIOSが格納されているマザーボード上のROMが
マップされている。つまり、電源投入直後にはBIOSが実行される。BIOSはハードウェア
の検出や初期化 (Power On Self Test) を実行した後、ブートローダを探索する
処理に入る。
ブートローダは、ディスクの先頭セクタにある Master Boot Record (MBR) という領域に収められ ている。MBRは下記のような構造になっている。
- ブートローダ (446B)
- パーティションテーブル (64B)
- Boot Signature (2B)
MBRの探索は、優先順位の高いブートデバイスから順に、そのデバイスの先頭1セクタの
末尾2バイトが0x55 0xaa
(Boot Signature) であるか確認することによって行われ
る。BIOSは、Boot Signatureが存在するセクタを発見すると、そのセクタの内容を
0x7c00
番地にロードし、実行を移す。
2. ブートローダ (GRUB)
ブートローダはOSをディスクからメモリに読み込み、起動する役目を持ったプログラム である。Linuxでは一般的にGRand Unified Bootloader (GRUB) というブートローダが用いられること が多い。GRUB以外にも、 Syslinux やLILOなどのブートローダが用いられている。 GRUBは複数の Stage と呼ばれるプログラムに分かれている。
Stage 1
GRUBのStage 1は、MBRの先頭446Bに存在する。Stage 1の役割は、Stage 2 (Stage 1.5) をロードすることである。1セクタしかないMBR内でブートローダの処理を全て実現する ことが難しいため、このような仕組みになっている。
Stage 1.5
Stage 1.5はの役目は、ディスク上のファイルシステムを解釈し、Stage 2をロードする ことにある。Stage 1.5は、ディスク上ではMBRと最初のパーティションの間に存在する、 DOS Compatibility Regionという領域に収められており、は以下のように解釈する ファイルシステムごとに存在する。
$ ls -lah /boot/grub | grep stage1_5
-rw-r--r--. 1 root root 14K 3月 13 22:41 2014 e2fs_stage1_5
-rw-r--r--. 1 root root 13K 3月 13 22:41 2014 fat_stage1_5
-rw-r--r--. 1 root root 12K 3月 13 22:41 2014 ffs_stage1_5
-rw-r--r--. 1 root root 12K 3月 13 22:41 2014 iso9660_stage1_5
-rw-r--r--. 1 root root 13K 3月 13 22:41 2014 jfs_stage1_5
-rw-r--r--. 1 root root 12K 3月 13 22:41 2014 minix_stage1_5
-rw-r--r--. 1 root root 15K 3月 13 22:41 2014 reiserfs_stage1_5
-rw-r--r--. 1 root root 12K 3月 13 22:41 2014 ufs2_stage1_5
-rw-r--r--. 1 root root 12K 3月 13 22:41 2014 vstafs_stage1_5
-rw-r--r--. 1 root root 14K 3月 13 22:41 2014 xfs_stage1_5
Stage 2
Stage 2は、GRUBの本体である。これまでのstageでファイルシステムを読み込めるよう になっているので、stage 2はファイルシステム上に普通のファイルとして置かれている。
$ ls -lah /boot/grub/stage2
-rw-r--r--. 1 root root 124K 3月 13 22:41 2014 /boot/grub/stage2
Stage 2はmenu.lst
というファイルを読み込み、インストールされているOSの
一覧を得る。これを元に、普段目にするOSの選択画面が表示される。下記は、
menu.lst
の例である。
# grub.conf generated by anaconda
#
# Note that you do not have to rerun grub after making changes to this file
# NOTICE: You have a /boot partition. This means that
# all kernel and initrd paths are relative to /boot/, eg.
# root (hd0,0)
# kernel /vmlinuz-version ro root=/dev/mapper/VolGroup-lv_root
# initrd /initrd-[generic-]version.img
#boot=/dev/vda
default=0
timeout=5
serial --unit=0 --speed=115200
terminal --timeout=5 serial console
title CentOS (2.6.32-573.18.1.el6.x86_64)
root (hd0,0)
kernel /vmlinuz-2.6.32-573.18.1.el6.x86_64 ro root=/dev/mapper/VolGroup-lv_root rd_NO_LUKS LANG=en_US.UTF-8 rd_NO_MD rd_LVM_LV=VolGroup/lv_swap SYSFONT=latarcyrheb-sun16 crashkernel=auto console=ttyS0,115200n8 rd_LVM_LV=VolGroup/lv_root KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM
initrd /initramfs-2.6.32-573.18.1.el6.x86_64.img
title CentOS (2.6.32-573.12.1.el6.x86_64)
root (hd0,0)
kernel /vmlinuz-2.6.32-573.12.1.el6.x86_64 ro root=/dev/mapper/VolGroup-lv_root rd_NO_LUKS LANG=en_US.UTF-8 rd_NO_MD rd_LVM_LV=VolGroup/lv_swap SYSFONT=latarcyrheb-sun16 crashkernel=auto console=ttyS0,115200n8 rd_LVM_LV=VolGroup/lv_root KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM
initrd /initramfs-2.6.32-573.12.1.el6.x86_64.img
ユーザがどれか1つのOSを選択すると、GRUBはそのOSのカーネルvmlinuz-*
と
RAMディスクinitramfs-*.img
をメモリ上にロードし、カーネルの先頭アドレスに
ジャンプし、その役目を終える。
3. カーネル
ブートローダから起動されたカーネルの実行バイナリ vmlinuz
は、低レベルな初期
化処理を実行した後、カーネルの本体を自己解凍し、メモリにロードする。次に、
カーネルの本体の先頭アドレスにジャンプした後、さらに様々な初期化処理を実行する。
ここら辺の仕組みは、
linux-insides と
いう資料が詳しい。また、Linuxではないものの、30日でできる! OS自作入門 という書籍も勉強になった。
カーネルは全ての初期化処理を終えると、初期RAMディスク (initrd) を展開し、 仮のルートファイルシステムとしてマウントする。初期RAMディスクには、 本番のルートファイルシステムが置いてあるディスクをマウントするために必要な ドライバや、各種ユーティリティが含まれている。
initrdには、主にinitrdとinitramfsの2形式があり、現在では後者が使われることが多い。
initramfsは、ルートディレクトリをcpioという形式でアーカイブし、
gzipで圧縮したものである。実際に /boot
にあるinitramfsの中身を覗くためには、
次のようにすれば良い。
$ zcat initramfs-xxx.img | cpio -idv
CentOSやFedoraでは、initramfsの先頭にCPUマイクロコードが入っているので、これを スキップしてから解凍する。
$ /usr/lib/dracut/skipcpio initramfs-xxx.img | gunzip -c | cpio -idv
ファイルリストを見るだけなら、lsinitrd
というコマンドもある。
なぜtarでなくcpioなのか気になったので調べたところ、initramfsのドキュメンテーションに 答えが 書いてあった。
- cpioは標準化されている
- cpioは既にLinuxで広く使われている (rpmの内部など)
- cpioの方がtarより単純で綺麗 (なので作成・展開ともに容易)
などの理由があるそうだ。
4. init
カーネルはinitrdのマウントに成功すると、initrdに含まれているの /sbin/init
というファイルを実行する。Initは実行される最初のプロセスであり、各種デーモン
など、他のプロセスを起動する。InitのPIDは常に1である。Initには、
SysVinit,
Upstart,
Systend
など様々な実装があるが、最近のCentOS, Fedora, Ubuntuなどのディストリビューション
では、Systemdが採用されている。