Linuxのブートローダ

ブートローダは、ディバイスをファイルシステムとして認識し、
ユーザの指定したOS(カーネルイメージ)をメモリ上へロードするプログラムである。

ここでは、代表的なブートローダであるGRUBを例に挙げる。
LILOについては、別ページ「LILO」を参照して頂きたい。

起動の流れ

ここでは、GRUB(GNU-GRand Unified Bootloader)のver.0.92を例にカーネルイメージへ制御を渡すまでのシーケンスを説明する。

MBRは1セクタ分の容量しか持たないため、多くの機能を持ったプログラムを配置することができない。そこでGRUBを中心としたブートローダは、自身のプログラムを2つに分けて構成し、MBR上とディバイス内部の2箇所に配置する。
これにより、容量の制約を受けずにブートローダを作成することを可能とした。

ステージ2の配置位置によっては、CPUの制約を受けロードが行えない。そこで、ステージ1.5という、ステージ2への踏み台となるプログラムを置き対処することもある。(ここでは割愛する。)

したがって、ステージ2の配置されるプログラムは「/boot」上に置かれ、制約を受けない領域に配置するためパーティションを分けて、ディバイスの先頭に置くことが推奨される。

stage1(ステージ1)

ステージ1は、BIOSから制御が渡される。BIOSはMBRの内容をチェックするためにメモリ上へ1セクタ分のデータを読みこんでいるため、MBR内に配置されているステージ1は、既にロード済みである。

先述した通り、ステージ1はサイズが小さい。制限により、512バイトの固定長である。

# ls -l /boot/grub/stage1
-rw-r--r--. 1 root root 512 2009-12-05 19:29 /boot/grub/stage1

ステージ1の目的は、ステージ2をロードし制御を渡すことである。
(厳密には、ステージ2の先頭を発見しメモリ上へロードする)
ソースコードも1ファイルと非常に小さいので、GRUBver0.92を参考にそのプロセスを説明する。

▼GRUBver0.92を元にしたステージ1の実行プロセス。
linux_01.gif

MBRの指定のアドレス「_start」へ制御が渡されると、
まず、BPBとブートローダのパラメータが格納された領域の後へジャンプする。

46   .globl _start; _start:
55
56       jmp    after_BPB
57       nop    /* do I care about this ??? */

以下は、BPB(BIOS Parameter Block)である。
BPBとは、BIOS側からファイルシステムの状態を認識するために参照する領域である。この領域は、BIOSによりファイルシステムを認識する際に利用されるため、あくまで領域を確保しているのみである。

59       /*
60        * This space is for the BIOS parameter block!!!!  Don't change
61        * the first jump, nor start the code anywhere but right after
62        * this area.
63        */
64   
65       . = _start + 4
66   
67       /* scratch space */
68   mode:
69       .byte    0
70   disk_address_packet:    
71   sectors:
72       .long    0
73   heads:
74       .long    0
75   cylinders:
76       .word    0
77   sector_start:
78       .byte    0
79   head_start:
80       .byte    0
81   cylinder_start:
82       .word    0
83       /* more space... */
84
85    . = _start + STAGE1_BPBEND
86
87       /*
88        * End of BIOS parameter block.
89        */

次に、ブートローダのパラメータ、メモリレイアウト情報を保持する領域。
stage2_addressの内容から、0x8000へステージ2がロードされることがわかる。
ステージ1が0x07C0なので、かなり近い領域に配置されることになる。
(これは、ステージ1が固定長であるため、遠くに配置する必要がないからであると言われている。)

91    stage1_version:    
92        .byte    COMPAT_VERSION_MAJOR, COMPAT_VERSION_MINOR
93    boot_drive:    
94        .byte 0xff    /* the disk to load stage2 from */
95                /* 0xff means use the boot drive */
96    force_lba:
97        .byte    0
98    stage2_address:
99        .word    0x8000
100   stage2_sector:
101       .long    1
102   stage2_segment:
103       .word    0x800

その後、ステージ1の処理の本体が実行される。
(この処理は、アドレス「0x07C0」へ格納される。)

105   after_BPB:
106   
107   /* general setup */
108       cli        /* we're not safe here! */
109   
110       /*
111        * ljmp to the next instruction because some bogus BIOSes
112        * jump to 07C0:0000 instead of 0000:7C00.
113        */
114       ljmp    $0, $ABS(real_start)
115   
116   real_start:    
117   
118       /* set up %ds and %ss as offset from 0 */
119       xorw    %ax, %ax
120       movw    %ax, %ds
       ・・・(略)

BIOSのint 0x13命令(割り込み命令)を用いてディスク上のステージ2が置かれたセクタをメモリ上へロードする。(int 13命令の利用)
最後に、ロードされたステージ2(0x8000)へ制御を移す。

358       /* boot stage2 */
359       jmp    *(stage2_address)

stage2(ステージ2)

ステージ2は対話的に設定を行える高度なシェル機能や多種のファイルシステムを認識する。それ故に、プログラムのサイズは大きい。

# ls -l /boot/grub/stage2
-rw-r--r--. 1 root root 125432 2009-12-05 19:29 /boot/grub/stage2

ステージ1は1セクタ分のプログラムのみしかロードしない。そこで、まず初めにステージ2のプログラム全てをロードする。
ステージ2の先頭に、stage2の配置されたセクタの位置が記録されているので、これを元に残りのプログラムをロードする。

その後、メイン処理が置かれた0x8200へ制御を渡し、処理を実行する。

ステージ2では、ファイルシステム上に配置された設定ファイル(/boot/grub/menu.lst)を参考にしたり、またブートプロンプトを通じて対話的にユーザから指定された情報を元にして、メモリ上へロードするカーネルイメージ、ルートディバイスを把握する。

ロードするカーネルイメージや付随するオプション情報が決定されると、GRUBはファイルシステムを通じて、カーネルイメージの先頭部へアクセスを行う。

Linuxのカーネルイメージは圧縮されている。
圧縮形式にはzImageとbzImageの2種類があり、GRUBはカーネルイメージの先頭部を参照して、これらの判別を行う。

GRUBのkernelパラメータで指定したカーネルイメージは、Linuxにおいては、純粋なカーネル本体そのものではない。
イメージには、解凍を行う為のアルゴリズムや、ブート処理に必要なパラメータも含んでいるからである。
厳密さを持たせるため、ここではブートイメージと呼ぶこととする。
(自己展開型ファイルのような構成をとっている。)

ブートイメージのロードを完了させると、GRUBはブートイメージの初期化ルーチンへ制御を渡す。

情報参照


バージョンの確認

grub-installコマンドを用いる。

# grub-install -v
grub-install (GNU GRUB 0.97)

構築・インストール

grubは先述したとおり、殆どのディストリビューションから利用されている。
そのため、インストールされていないことはほぼ無いと考えられる。

パッケージの入手

grub.i686は、Redhat系ディストリビューションであれば以下のコマンドで入手、インストールできる。

# yum install grub

ただし、grubのインストールはLinuxに対してでなくディバイス(MBR)に対して行う必要がある。
したがって、別途インストール作業を行う必要がある。

ブートディバイスへのインストール

GRUBをディバイスへインストールには、コマンド「grub-install」を用いる。

# grub-install --root-directory=/ /dev/hda 

これにより、第2引数に指定されたディバイスをブートディバイスとし、そのMBRへブートローダのステージ1をインストールする。

第1引数についてはオプションであり、設定ファイルの置かれたルートディレクトリを指している。これは、「/boot」が独立したパーティションに置かれている場合のみ、必要となるオプションである。


GRUBの設定

GRUBは、menu.lstによる設定から、シェルによる対話的にカーネルイメージの選択やオプションの指定が行える機能を備えている。

menu.lstによるカーネルイメージの選択

先述したとおり、GRUBはカーネルイメージを選択する際に、「/boot/grub/menu.lst」を参考にする。
(古い設定ファイルの場合は、「grub.conf」という名称である。)

設定は以下のイメージである。
linux_02.gif

# 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/sda3
#          initrd /initrd-[generic-]version.img
#boot=/dev/sda
default=0
timeout=0
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
title Fedora (2.6.31.5-127.fc12.i686.PAE)
        root (hd0,0)
        kernel /vmlinuz-2.6.31.5-127.fc12.i686.PAE ro root=UUID=314a8659-c4a6-40e1-befc-7b7e1bf99ec9 ・・・(略)
       initrd /initramfs-2.6.31.5-127.fc12.i686.PAE.img

GRUBでは、起動時にカーネルイメージを選択する画面が表示される。
その際の設定として、以下のパラメータが存在する。

default=0
カーネルイメージは複数選択できる。
defaultパラメータは、既定で選択されるカーネルイメージを指定する。
titleパラメータにより与えられたエントリを指す。

timeout=0
選択画面を表示する時間である。
タイムアウトすると、defaultで設定されたエントリが指定される。

splashimage=(hd0,0)/grub/splash.xpm.gz
選択画面で背景に表示する画像である。

hiddenmenu
このパラメータを指定すると、選択画面が表示されなくなる。

title Fedora (2.6.31.5-127.fc12.i686.PAE)
エントリの名前を指定し、かつエントリの区切りであることを表す。

       root (hd0,0)
ブートイメージの格納されている場所を表す。
第1引数hd0は物理ディスクを指し、第2引数はパーティションを指す。
0から始まる点に注意が必要である。

       kernel /vmlinuz-2.6.31.5-127.fc12.i686.PAE ro root=UUID=314a8659-c4a6-40e1-befc-7b7e1bf99ec9 ・・・(略)
ブートイメージの位置とカーネルパラメータを指定する。

      initrd /initramfs-2.6.31.5-127.fc12.i686.PAE.img
初期RAMディスクファイルの位置を指定する。これの目的については、後に説明する。

chainloader +1
例には出てこなかったが、別のブートローダを呼び出す場合には、このようなパラメータも存在する。これはWindows系OSを起動する際に、専用のブートローダであるNTLDRを呼び出すために用いる。
ブートローダからブートローダへ制御を渡すことを、内部的にもchainと表現するため、このような名称になったと考えられる。
(ステージ1.5からステージ2へ制御を渡す際も、chain_stage2というルーチンを呼び出す。→「grub-0.92/stage2/stage1_5.c」を参考)

WindowsXP等を起動イメージに追加する場合、以下のようなパラメータを設定するのが定石である。

title WindowsXP Pro(SP2)
    root (hd0,0)
    chainloader +1

ブートシェルによるカーネルイメージの選択

bashライクなインタフェースにより、カーネルイメージを選択することが可能である。
選択画面にて「c」をタイプすると、プロンプトが出現し、ユーザの入力を待ち受ける。

tabによる補完機能も兼ね備えているため、ブートローダの仕組みさえ理解しておれば、詳細な構文を理解していなくても容易に操作が行えるようになっている。

ここで利用する構文については、menu.lstとほぼ同じである。
ただし、パラメータを渡した後で、「boot」を入力しカーネルイメージのブート開始を明示的に指示する必要がある。

例えば、先ほどのWindowsXPの起動イメージを呼び出す場合は、以下のコマンドとなる。
パラメータ「title」は不要であるため、記述しない。

grub> root (hd0,0)
grub> chainloader +1
grub> boot

grubシェルによるmenu.lstの編集

Linux上でgrubコマンドを実行すると、menu.lstを対話的なインタフェースを通じて記述できる。このコマンドは、ブートシェルの応用で実現可能だが、tabによる入力補完が効かないのが難点である。

# grub
Probing devices to guess BIOS drives. This may take a long time.


    GNU GRUB  version 0.97  (640K lower / 3072K upper memory)

 [ Minimal BASH-like line editing is supported.  For the first word, TAB
   lists possible command completions.  Anywhere else TAB lists the possible
   completions of a device/filename.]
grub>

入力された情報を、「savedefault」により反映する。
WindowsXPを例にすると、以下のようになる。

grub> title WindowsXP Pro(SP2)
grub> root (hd0,0)
grub> chainloader +1
grub> savedefault

GRUBによる起動オプションの設定


GRUBでは、選択画面にて「e」をタイプすると、起動オプションが渡せる。

grub edit> kernel /boot/vmlinuz-2.6.31.5-127.fc12.i686.PAE mem=512M s

起動オプションの代表的なものとしては、以下が挙げられる。
起動オプション 内容
init 任意による初期プログラム
mem=xxx カーネルの認識するメモリサイズ
nosmp シングルプロセッサとして動作
noht HTを無効化
maxcpus CPU数
root ルートファイルシステム
read-only ルートファイルシステムを読み込み専用でマウント

これらは、Linuxのカーネルに対し渡されるオプションである。
もし、カーネルに処理できない場合は、後述するinitプロセスに引数として渡される。

  • 最終更新:2009-12-26 04:11:22

このWIKIを編集するにはパスワード入力が必要です

認証パスワード