Linuxのブートイメージ

カーネルイメージは、C言語(もしくは、アセンブラ)で記述されたソースコードをコンパイルすることで得られ、
ブートローダによってメモリ上へ配置し、制御を渡されることでオペレーティングシステムとして機能する。

しかし、このイメージファイル(例:vmlinuz-2.6.31.5-127.fc12)は、圧縮されたカーネルイメージ、ブートパラメータ、圧縮アルゴリズム、初期化を行うためのプロセス(以下、初期化ルーチンと呼ぶ)等で構成され、純粋なカーネルイメージとは言い難い。

したがって、一部書籍はこれを、ブートイメージと呼ぶ。
本サイトでは、誤解釈を避けるため、この用語を標準的に用いる。

なお、ここで説明されるブートイメージは、ファイルシステム上(/boot)に配置され、ブートローダを通じてユーザにより選択されるイメージファイルである。
メモリ上へ配置された動作中のカーネルについては、別頁「Linuxのカーネルの情報参照」を参照して頂きたい。


初期化ルーチンの流れ

Linuxのブートイメージは、ブートローダによってメモリへロードされ、0x100000から始まる初期化ルーチンを通じて制御を渡される。その後、様々な処理を経て、メモリ上のカーネルイメージの初期化ルーチンへ制御を渡す。
ここでは、この時の行われる処理の流れについて説明する。

ブートイメージは、ブートローダによってロードされ制御を渡されても、すぐにカーネルの処理が実行されるわけではない。
BIOSのサービスを利用してハードウェアの情報を取得したり、また各種コントローラの初期化、また圧縮されたカーネルイメージの展開を行う必要がある。

カーネルイメージへ制御を渡す前の処理(初期化ルーチン)は、ハードウェアアーキテクチャやOSの思想によって、その扱われ方は様々である。例えばWindows系では、ブートローダであるNTLDRはWindows系のみをサポートすることを前提とすれば良いためか、それらの処理を全て担っている。

Linuxの場合は、イメージ側が自身の前処理を行うためのルーチンを持つスタイルをとっているが、これはブートローダ側の汎用性を保つための処置であると考えられる。しかしながら、これらのルーチンのメモリ配置位置等の情報はブートローダでないと知り得ないため、イメージ側が処理ルーチンを持ってはいるものの、厳密にはブートローダの行う作業の一部であるとみなせる。

初期化ルーチンはメモリ上のアドレス0x100000に配置され、ブートローダからのジャンプ命令により制御が渡される。

このルーチンではまず初めに、BIOSのサービスを通じて、ハードウェアの情報を取得する。
具体的には、ハードディスクやマウス、ビデオカードからAPM、EDDといった、BIOSのPOSTよりもさらに高度なものである。これらは、メモリ上のアドレス0x90000へ格納される。

また、キーボードやなどのコントローラの初期化や画面モードの指定も行う。
これもBIOSのそれよりも、さらに高度なものとなっている。

このフェーズでは、画面上に画面モードの指定を行うためのインタフェースも設け、画面のサイズ変更を行うことを可能としている。

次に、初期化ルーチンはカーネルイメージの解凍を行う。

ブートローダであるGRUBの場合、BIOSのサービスを利用するためにCPUをリアルモードで動作させたり、また制約を回避するためにプロテクトモードで動作させる等、このような処理を切替ながら行ってきた。

しかし、ここから先の処理ではBIOSのサービスは不要であり、また解凍ルーチンでカーネルイメージを展開する際に制約が邪魔になるため、常時プロテクトモードにより動作させる。
(※これはモダンなAIに限定される。筆者はzImage方式を調査していないため、bzImage方式に限定して話を進める。)

また、この辺りからようやくC言語らしい記述を可能とするため、メモリの初期化を行うようになる。
例えば、大域領域や静的変数を扱うためBSS的役割を持ったルーチンを走らせたり、関数呼び出しを実現するためのユーザスタックの確保を行う。(ブートローダのステージ1で行っていた初期化はハードウェアスタックに対するものであり、これはあくまで割り込みへの対処策であった。)

bzImageのアルゴリズムは、gunzipコマンドと同じアルゴリズムによって解凍が行われる。ここでは、単に解凍を行うだけでなく、これまでブートローダの起動などによって利用された領域を上書きなどを行い、メモリの節約を行う。

その後、カーネルの初期化ルーチンへ制御を渡す。


情報参照

ここでは、バージョンの確認方法、ブートイメージの配置位置について説明する。

バージョンの確認

・ファイル名の参照
ブートイメージのバージョンは、そのままブートイメージのファイル名となる。
したがって、以下のコマンドによって確認が可能である。

# ls /boot/vmlinuz*
/boot/vmlinuz-2.6.21-1.3228.fc7  /boot/vmlinuz-2.6.23.17-88.fc7  /boot/vmlinuz-2.6.21-7.fc7xen 

・カーネルソースコードのMakefile
ブートイメージのコンパイル元のソースコードがある場合は、Makefileからバージョンを確認できる。
先頭4行にその情報が記述されているため、headコマンドなどを用いれば良い。

# head -4 /usr/src/linux/Makefile
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 23
EXTRAVERSION = .17-88.fc7


ブートイメージの配置位置

ブートイメージは、先述した通りファイルシステム上に置かれている。

# ls /boot/vmlinuz*
/boot/vmlinuz-2.6.21-1.3228.fc7  /boot/vmlinuz-2.6.23.17-88.fc7  /boot/vmlinuz-2.6.21-7.fc7xen 

「vmlinuz-バージョン」となっており、この中から任意のブートイメージを選択できる。

かつてのx86は、最大1Mのメモリ空間でしか活動が行えなかった。ビデオメモリを配慮すると、さらに小さな領域しか扱えなくなるため、圧縮されたカーネルイメージが512Kバイト以下で収まることを前提に設計を行う必要があった。そこで利用されたのが、zImage形式という圧縮化されたブートイメージフォーマットである。しかし、近年カーネルは様々な機能をサポートし肥大化したため、このサイズには収める事ができなくなった。そこで、拡張BIOS命令を用いて、この制限を解除して解凍処理が行えるような方法も考えられた。これはbzImage形式と呼ばれる。

拡張BIOS命令は、全ての環境でサポートされるわけではないので、zImageのニーズは0ではない。
しかし、近年のカーネルイメージのサイズはどれだけ削っても512Kバイトに収まることは難しいため、bzImageが主流である。


構築・インストール

ここでは、ブートイメージの作成とインストールについて説明する。
ブートイメージを作成する際は、同一バージョンのカーネルモジュールもコンパイルしインストールするのが普通なので、これも加えて説明する。

なお、ここでインストールされたブートイメージを利用するには、ブートローダからロードされ制御を渡される必要がある。
したがって、ここでは解説しないが、ブートローダの設定も必要となる。
また、必要に応じて初期RAMの設定も必要となる。
別途、参照して設定していただきたい。

カーネルソースコードの入手

新たな機能を追加が必要であったり、あるいはセキュリティを高める必要がある場合、このソースコードに対して修正を行い、コンパイルして新たにカーネルのイメージファイルを作成し、再起動してメモリ上へ配置する。

しかし、最近のディストリビューションでは、ソースコードを添付しないものが出てきた。
このような場合、ディストリビューションの配布元のWEBサイトをあたるか、あるいはリポジトリを探すなどして、カーネルのソースコードを入手する必要がある。

入手方法はいくらでも存在するが、ここでは本家のサイト「http://www.kernel.org/」を紹介する。
ここで手に入る、ディストリビュータの手が入っていない純粋なカーネルのことを、バニラカーネルという。

wgetコマンドなどを用いて、入手すると良い。圧縮されているので、tarコマンドなどで解凍を行う。

# wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.31.6.tar.bz2
--2009-12-07 20:15:33--  http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.31.6.tar.
bz2www.kernel.org をDNSに問いあわせています...
・・・(略)

# tar xvf linux-2.6.31.6.tar.bz2
・・・(略)

解凍されたプログラムは、以下のように配置される。

# ls
linux-2.6.31.6 linux-2.6.31.6.tar.bz2

# cd linux-2.6.31.6
# ls
COPYING        MAINTAINERS     arch     firmware  ipc     net       sound
CREDITS        Makefile        block    fs        kernel  samples   tools
Documentation  README          crypto   include   lib     scripts   usr
Kbuild         REPORTING-BUGS  drivers  init      mm      security  virt

概要としては、以下の通りである。

ディレクトリ名 内容
arch i386などのアーキテクチャ依存のコード。
block ブロックディバイス関連のコード
crypto 暗号化・復号関連のアルゴリズムのコード
drivers ディバイスドライバ関連のコード
fs ファイルシステム関連のコード
include ヘッダファイル
init 初期化関連のコード
ipc SystemV互換のプロセス間通信関連のコード
kernel カーネル機能関連のコード
lib モジュール関連のコード。
mm メモリ管理機能関連のコード。(→Memory Management)
net ネットワークプロトコル関連のアルゴリズム等のコード
scripts 先述したpatch-kernel等、カーネル再構築の補助関連のスクリプト
security セキュリティ関連のコード
sound サウンド関連のコード

カーネルソースコードの変更

先述したとおり、カーネルの修正はソースコードの編集によって行える。
管理者がソースコードを直接編集するのも手であるが、どちらかと言えばパッチの適用が現実的である。

今回は、2.6.31.5-127.fc12で動作する現状のカーネルを2.6.31.6へバージョンアップするのが目的であり、
全てのソースコードをダウンロードしているのでパッチは不要である。

しかしこれでは説明にならないので、今回は自前でパッチファイルを作成した。

-rw-r--r--. 1 root root 605 2009-12-07 21:28 kwkw.diff

これは、起動時のシステムメッセージに、サイトのタイトルを出力させる修正パッチである。
機能的な追加は全く無い。

パッチの適用を行う際には、まずパッチの先頭部を確認し、
どのディレクトリ(深さ)に対して適用するのかを確認する。

# pwd
/(略)/init
# head -1 kwkw.diff
diff -Naur old_init/main.c init/main.c

カーレンとディレクトリは./initというディレクトリであり、パッチの対象は./init/main.cである。
したがって、パッチを当てる際には、一つ上のディレクトリから実行する必要がある。

カーレントディレクトリをひとつ上のディレクトリへ移動させpatchコマンドを実行することもできるが、
オプション「-p」を用いて、以下の通りコマンドを入力すれば、パッチを移動させることなく適用が可能となる。

# patch -p1 < kwkw.diff
patching file main.c

上記のメッセージを確認できると、パッチは適用されたものと判断できる。

カーネルコンフィギュレーションの反映

カーネルコンフィギュレーションとは、カーネルの持つ機能やモジュールの有効/無効等の設定を行うことである。
Linuxの場合、モジュール化された機能をカーネルベースへ組み込んだり、またローダブルモジュールとして実行時に付け外しが行えるようにするといったことが可能であるため、これらの設定がカーネルのコンパイル時に必要となる。

この設定ファイルは、「.config」という名称で、カーネルソースコードのトップのディレクトリに配置される。

パッチの適用などにより新しいバージョンに変化した際には、新たなカーネルモジュールや機能が追加されている可能性があるため、これらを再設定する必要がある。

カーネルソースコードのトップのディレクトリへ移動し、以下のコマンドを実行し設定の入力を行う。

# make config
scripts/kconfig/conf arch/x86/Kconfig
*
* Linux Kernel Configuration
*
*
* General setup
*
Prompt for development and/or incomplete code/drivers (EXPERIMENTAL) [Y/n/?] Y
Local version - append to kernel release (LOCALVERSION) []
Automatically append version information to the version string (LOCALVERSION_AUTO) [N/y/?] N
・・・(略)

  • Y - カーネルベースへ組み込む
  • M - カーネルモジュール(ローダブルモジュール)化する
  • N - 組み込まない

カーネルコンフィギュレーションは大量である。(2.6.23.17-88.fc7-i686でも約3500行ある。)
そのため、新しいカーネルコンフィギュレーションへ古いコンフィギュレーションを一から再設定するのは、負荷が大きい。

そこでmakeユーティリティは、「oldconfig」というオプションを提供する。
これを実行すると、現状の設定については現状のままとし、新しく追加された機能のみについて設定を行える。

# cp (現在の設定ファイル) .config
# make oldconfig
scripts/kconfig/conf -o arch/x86/Kconfig
#
# configuration written to .config
・・・(略)

カーネルの名前(カーネルバージョン)の設定

既存のMakefileを使い回した場合、既存のカーネルと作成するカーネルの名前(カーネルバージョン)が競合する。
生成されるイメージやディレクトリ等の名称として用いられるため、一意である必要がある。

名前は、Makefileの上位4行によって決定される。
例えば、下記の設定であった場合、「vmlinuz-2.6.31」という名称になる。

# head -4 Makefile
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 31
EXTRAVERSION =

そのため、作成を行う都度、名前が競合しないよう修正を行う必要がある。
上位3行についてはバージョン情報であるため、カーネルのバージョンに合わせて設定するのが望ましい。
同一バージョンでいくつかブートイメージを作成するのであれば、4行目のEXTRAVERSIONを変更すると良い。

# head -4 Makefile
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 31
EXTRAVERSION = -fugafuga

カーネルのコンパイル(ブートイメージの作成)

makeは前のコンパイル情報をキャッシュするため、誤動作しないよう先にクリアを行う。

# make clean
 CLEAN   arch/x86/boot/compressed
 CLEAN   arch/x86/boot
・・・(略)

以下のコマンドを実行すると、先ほど設定したコンフィギュレーションを元にモジュールを選択してカーネルソースコードのコンパイルを行う。
この時同時に、カーネルイメージは圧縮され、また初期化ルーチン等も加えられ、ブートイメージとして生成される。
カーネルソースコードは大量であるため、これには多くの時間を要する。

# make
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/basic/docproc
  HOSTCC  scripts/basic/hash
  HOSTCC  scripts/kconfig/conf.o
  HOSTCC  scripts/kconfig/kxgettext.o
  HOSTCC  scripts/kconfig/zconf.tab.o
  HOSTLD  scripts/kconfig/conf
scripts/kconfig/conf -s arch/x86/Kconfig
  CHK     include/linux/version.h
  CHK     include/linux/utsrelease.h
  UPD     include/linux/utsrelease.h
・・・(略)
  LD      arch/x86/boot/setup.elf
  OBJCOPY arch/x86/boot/setup.bin
  OBJCOPY arch/x86/boot/vmlinux.bin
  HOSTCC  arch/x86/boot/tools/build
  BUILD   arch/x86/boot/bzImage
Root device is (8, 3)
Setup is 13532 bytes (padded to 13824 bytes).
System is 3525 kB
CRC dafb4595
Kernel: arch/x86/boot/bzImage is ready  (#2)

ちなみに、ここでzImage形式かbzImage形式を明示することも可能である。
zImageの場合は、以下の通り。

# make zImage

bzImage形式の場合は、以下の通り。

# make bzImage

完成したブートイメージは、「arch/i386/boot」へ「bzImage」という名称で配置される。

カーネルモジュールのコンパイル

カーネルコンフィギュレーションでローダブルなカーネルモジュールとして選択されたものについては、別途コンパイルを行う必要がある。これはカーネルとは独立したプログラムとして別途コンパイルを行う必要があるからである。

コンパイルは、以下のコマンドによって実行できる。
これは、特にドライバの数が半端なく多いため、カーネルイメージよりも多くの時間を要する。

# make modules
  CHK     include/linux/version.h
  CHK     include/linux/utsrelease.h
  SYMLINK include/asm -> include/asm-x86
  CALL    scripts/checksyscalls.sh
  AS [M]  arch/x86/crypto/aes-i586-asm_32.o
  CC [M]  arch/x86/crypto/aes_glue.o
・・・(略)

ブートイメージの配置(インストール)

カーネル(ブートイメージ)のコンパイルが完了したのであれば、次にインストールを行う。
以下のコマンドを実行し、/boot下に正しくファイルを配置する。

# make install
sh /usr/src/kernels/2.6.23.17-88.fc7-i686/arch/i386/boot/install.sh 2.6.31-fugafuga arch/i386/boot/bzImage System.map "/boot"
・・・(略)

伝統的に上記のようにコマンドを用いて配置するのが一般的であるが、実際はブートイメージを指定ディレクトリへ配置するだけなので、以下のコマンドで代替することも可能である。

# cp arch/x86/boot/bzImage /boot/vmlinuz-2.6.31-fugafuga
# cp System.map /boot/System.map-2.6.31-fugafuga

カーネルモジュールの配置(インストール)

カーネルモジュールのコンパイルが完了したのであれば、これもインストールを行う。
以下のコマンドを実行し、/lib以下の定められたディレクトリへ配置する。

# make modules_install

  • 最終更新:2009-12-08 21:46:35

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

認証パスワード