vmlinux

出典: フリー百科事典『ウィキペディア(Wikipedia)』
移動: 案内検索
Linuxカーネルブートならびに伸長プロセス

Linuxシステムにおいて、vmlinuxとは、内部にLinuxカーネル本体を包含する静的リンクされた実行ファイルである。ELFCOFF そしてa.outを含むLinuxでサポートされているオブジェクトファイルの一種である。vmlinuxファイルはカーネルデバッグ、カーネルのシンボルテーブル生成、またはその他の用途で利用されるが、このファイルを用いてオペレーティングシステムを起動することはできない。このファイルから全てのシンボルを取り除き、圧縮をかけ、マルチブート英語版用ヘッダ、ブートセクタBIOS用ローレベルブートセットアップルーチン、自己伸長ルーチンなどを追加することで始めてカーネルとして使用可能となりブートできる(このように圧縮をかけたり、ブートセットアップ用に付加的なコードを追加したカーネルのことをカーネルイメージ[1]と呼んで区別する場合もある)。

通常はカーネルのビルドが正常終了すると、(ビルド完了後には同名や類似した名前の中間ファイルが幾多もあるため注意が必要であるが、)ソースコードのトップディレクトリにこのファイルが存在する。

語源[編集]

古くから、UNIXのカーネルイメージはunixというファイル名であり、cp (作成したイメージのファイル名) /unixのようにコピーしてインストールされていた。BSD仮想記憶が実装され、仮想記憶をサポートしたカーネルであることを示すvm-という接頭辞を付けvmunixという名前が使われるようになった。vmlinuxという名前はそのvmunixを基にした命名法によるものである。さらにLinuxでは圧縮イメージという機能も追加され、vmlinuzという名前が付けられた。vmlinuxを圧縮したものであることを表す文字zzipped)を後尾に付している。

ロード[編集]

古くから、カーネルはファイルシステム階層標準(Filesystem Hierarchy Standard; FHS)におけるルートディレクトリ(/)に置かれることが多かった。それとは別に、しかしながらブートローダはハードディスクにアクセスするBIOSドライバを使用しなければならなかったため、いくつかのi386システムにおいて、ハードディスクの先頭1024シリンダー(Cylinder 1024)までしかアクセスできないという制限があった(BIOS割り込みルーチンINT 13H英語版(0x13)を参照せよ)。

この制限に対処するため、Linuxディストリビューターは、ユーザーにハードディスクの先頭にルートディレクトリ、とりわけブートローダやカーネル、そしてシステムブートに関連するファイルを置くパーティションを作成するよう推奨した。GRUBLILO、そしてSYSLINUXはその点に関し、共通したブートローダである。

慣習的に、このシステムブートに関連するファイルを配置するパーティションはファイルシステム階層における/bootというディレクトリにマウントされる。のちにこれはFHSにより標準化されており、このことを強制する技術的な制約などないが、今やLinuxカーネルイメージが/または/bootに存在する事を仮定している。詳しくは、FHSバージョン2.3のセクション3.5.2を参照してほしい。

圧縮[編集]

旧来より、ブート可能なカーネルイメージを生成した際、カーネルはまた、zlibアルゴリズムを用い圧縮されている。Linuxカーネルバージョン2.6.30[2]からはこれに加え、圧縮率が高いLempel-Ziv-Markov chain algorithmBZIP2アルゴリズムによる圧縮もサポートされている。システムに電源を投入し、BIOSがブートローダを読み込み、ブートローダが圧縮されたカーネルイメージコードを読み込む。このコードからカーネルコードが伸長(展開)されると、いくつかのシステムでは、ブートプロセスが進行するにつれコンソールドット(.)が何個も表示される。

圧縮ルーチンは、いくつかのアーキテクチャ、とりわけ、i386のようにブート時に読み込めるデータサイズが極端に制限される場合必須であったが、bzImageが開発された現在においては、ブートプロセスにおいては、取るに足らない要素である。

x86アーキテクチャとは異なり、SPARCアーキテクチャにおいては、カーネルイメージはvmlinuxファイル自体を単にgzipで圧縮したものである[3]。これはSPARC Linuxシステムにおいて使用される、SILO英語版ブートローダがgzipで圧縮されたイメージを透過的に伸長できるからである。

ブートイメージのファイル名はさほど重要ではなく(なぜなら近年のブートローダはイメージを設定ファイルで指定可能、もしくはブート時に指定可能だからである)、しかし、慣習的には、vmlinuz または zImageとなる場合が多い。

bzImage[編集]

Linuxカーネル開発が成熟するにつれて、ユーザーが生成するカーネルのサイズはいくつかのアーキテクチャにおいて課される制限である、圧縮されたカーネルコードが保持可能なサイズを越えるまでになってきた。

この制限に対処するため、カーネルから不連続メモリ領域をうまく分離した、bzImage (big zImage)フォーマットが開発された。

bzImageはzlibアルゴリズムを用い圧縮されている。また2.6.30[2]からは更によりよいアルゴリズムで圧縮可能である。もっともらしい誤解は、bzがファイル名の接頭辞にあるため、このイメージがbzip2で圧縮されているのだということが語られるが、そうとは限らない。(確かに、bzip2パッケージは"bz"の接頭辞のついたツールともに配布されている。例えば、bzip2で圧縮されたテキストファイルをlessページャを通して透過的に読むことができる、bzless、同様にcatコマンドに対するbzcatなどが付属している。)

カーネルイメージのビルドとフォーマット[編集]

Linuxカーネルイメージは通常、Linuxカーネルソースのディレクトリ(もしくはオプションを指定し、最終生成物を出力するディレクトリ)において次のコマンドを実行することで得られる。

make

現行のバージョン2.6では、アーキテクチャによって、このとき暗黙のうちに指定されるターゲットは異なる。例えばx86(i386ならびにx86-64)アーキテクチャにおいては、暗黙のうちに

make bzImage

と指定されている。これはbzImageを生成せよとの指令を発行したことになる。 正常にビルドが進むと、端末エミュレータに以下のように表示される(以下、i386をターゲットとしたバージョン2.6.38-rc2のビルドより一部抜粋した。ビルドプロセスはリリース毎、アーキテクチャ毎に多岐に渡るため、この環境での説明に限定する)。

カーネルに静的リンクされる全てのオブジェクトコードはvmlinux.oにリンクされる。これとは別に、全てのオブジェクトファイルからシンボルのみを取り出し、単一のELFセクションヘッダとして保持するオブジェクトコード.tmp_kallsyms2.o(数字はカーネルコンフィグレーションにより異なる)が生成される。この2つのファイルをリンクしたものが本記事の対象とする実行ファイルvmlinuxである。続いてこのファイルにnmコマンドをかけ、シンボルテーブルSystem.mapファイルを生成する(.tmp_System.mapは比較検査のため生成する)。続いて圧縮ルーチンを含めたカーネルイメージのブート用ルーチンのビルドが開始される。

(以下、x86アーキテクチャにおいて$(BITS)はビット数に読み替えてほしい)

カーネルの圧縮、カーネルイメージの伸長などに関するソースコードはarch/x86/boot/compressedに存在する。

arch/x86/boot/compressed/head_$(BITS).oはカーネルイメージのメモリアドレス前方に位置し、BIOSから得た低レベルなハードウェアの情報を処理するためのコードである。詳細はソースコードのarch/x86/boot/compressed/head_$(BITS).S[4]やそのエントリポイントstartup_$(BITS)、リンカスクリプトなどを参照せよ。

arch/x86/boot/compressed/misc.oは後方に位置するカーネル本体を含むコードをzlibなどのアルゴリズムを用いて伸長するコードである。有名なUncompressing Linux...[注釈 1]というコンソールの表示はこの処理において見られる(実際の処理はアーキテクチャ毎に様々で、BIOSや起動直後のシステムが認識可能なメモリアドレスの制限のため、段階的に複雑なメモリ配置を行いこれを実現している。詳しくは、arch/x86/boot/compressed/misc.c[5]decompress_kernelなどのキーワードを参考に処理内容を見てほしい)。

head_$(BITS).o, misc.oその他カーネルイメージの伸長後に利用する低レベルコードをビルドし終わると、piggy.oというオブジェクトコードと一緒にリンクされ、arch/x86/boot/compressed/vmlinuxという実行ファイルを生成する(このファイルは本記事で説明の対象としているvmlinuxではないことに注意せよ)。piggy.oは次のようなプロセスをたどり生成される。ディレクトリのトップにあるvmlinuxは、GNU Binutilsにより配布されるobjcopy[6]コマンドを利用し、.commentなど不要なELFセクションヘッダとシンボル情報を全て削除されたうえで実行ファイルarch/x86/boot/compressed/vmlinux.binに変換される。このファイルに圧縮をかけ更に専用のツールを用いて、vmlinux.bin.gz(圧縮アルゴリズムにより拡張子は異なる)をELFのセクションヘッダに埋め込んだ特殊なELFオブジェクトファイルが生成される。これがpiggy.oである(詳しくはarch/x86/boot/compressed/Makefile[7]を参照せよ)。

この後ブート用の低レベルコードのビルドが続く。カーネルのブートに関するコードは前述の圧縮・伸長コードも含め、arch/x86/bootに存在する。

arch/x86/boot/header.o[8]はカーネルイメージの先頭メモリアドレスに存在するコードである。エントリーポイント_startから開始されるこのコードにより前述した伸長用ルーチンを後ほど呼び出す。

  • このコードは以前のリリースにおける、2つのコードbootsect.osetup.oの一部を抜きだし、再構成したものである。ちなみにbootsect.oフロッピーディスク用のダイレクトブート用コードであったがバージョン2.6では、header.Sの一部にダイレクトブートできない旨のメッセージを表示するコードのみ残っているに過ぎない。setup.oの残部は細かく分けられ、一部は前述のhead_$(BITS).oにも含まれている。

このオブジェクトファイルも含め、ビデオBIOS用処理などの低レベルなコードがarch/x86/boot/setup.elfという実行ファイルとしてリンクされる。

ここまで正常に終了したならば、bzImageの完成は目前となる。arch/x86/boot/setup.elfarch/x86/boot/compressed/vmlinuxはobjcopyコマンドの特殊なオプションによりそれぞれELFバイナリからrawバイナリ(「生バイナリ」とも。リロケーション情報を削除し、メモリ上に直接展開・実行可能なコード。詳細はobjcopyのマニュアル[6]またはInfoドキュメント[9]を参照せよ)arch/x86/boot/setup.binarch/x86/boot/vmlinux.binに変換される。この二つをカーネルビルド時にしか使われない専用ツールで結合すると、arch/x86/boot/bzImageが完成する。

ビルドの終了メッセージの意味は次の通りである。 "Root device is (8, 3)"、これはルートディレクトリのマウントされているファイルシステムはメジャー番号8番、マイナー番号3番(デバイスファイル参照)にあることを予めカーネルイメージに埋め込んだことを示す(ルートファイルシステムがブートローダで指定されていない場合、デフォルトで使用される)。すなわちこれはSCSIディスクの第一番目のディスク先頭第3パーティション(/dev/sda3)にルートファイルシステムがあることを示す。次の2行はカーネルイメージのデータサイズを示している。CPUがリアルモード状態のときに使用されるセットアップコード("Setup")は、セクタサイズにきれいに収めるため、512の倍数となる15360バイトにパディングされたことを通知している。"System"はカーネル本体を含むコードでこの場合1MBをゆうに越え、2828kBとなっている。続いてイメージのCRCが検出され、これが初回(#1)のビルドであることを通知し、ビルドプロセスは終了する。

以上よりbzImageファイルは特殊なバイナリフォーマットを持っている: すなわち、主に次のデータを連結し、構成されていることが分かる。

bzImage = setup.bin + vmlinux.bin
  setup.bin <---(raw binary)--- setup.elf <--- header.o + main.o + more...
  vmlinux.bin <---(raw binary)--- head_(BITS).o + misc.o + more... + piggy.o
    piggy.o <---(compressed + embedded)--- vmlinux <--- vmlinux.o + .tmp_kallsyms2.o <--- *.o *.a
                                             `|
                                          System.map

ブートプロセス[編集]

bzImageが開発された経緯の通り、アーキテクチャによってはブート開始直後のメモリ領域は非常に限られている。bzImageは限られたメモリ領域を有効活用するため、ジャンプ命令などを巧みに利用しており、その処理は一般には複雑である[10]。x86システムのブートでは、BIOSから起動されたブートローダがカーネルイメージ(とinitrd)をメモリにロードする。このとき、カーネルイメージは前項の2セクションで分割した上でロードされる。"Setup"部分がブードローダの直後のメモリアドレスにBIOSなどの予約エリアを上書きしないようにロードされる。"System"はメモリアドレス0x100000(=1MB)から後方にロードされる(こちらはもとよりリアルモード下では原則アクセスできないはずなので、上書きの心配はない)。initrdは"System"より後方のメモリアドレスにロードされる。"Setup"はBIOS割り込みルーチンを駆使し、header.oなどの処理を経てhead_$(BITS).oコードに移行し、CPUをプロテクトモードに遷移する。続いて、head_$(BITS).oからmisc.oが呼び出されその後続のデータとなっているカーネル本体が伸長される。伸長後はより複雑な処理となる。以降の処理内容は順に述べるのみとし、必要ならばソースコードを参照して欲しい。伸長済みのカーネル本体に処理を移すと、

  • ハードウェアの低レベル初期化
  • 仮想記憶の有効化などを含むメモリ管理の開始
  • CPU認識と特定の処理実行、SMP関連の処理実行

ここまでは(インラインアセンブラを含む)アセンブラコードが多く含まれている。以降はアーキテクチャ非依存となる。

init/main.o[11]start_kernel()に処理が移り

を行い、システムのブートアップが完了する。


カーネルイメージの伸長[編集]

現時点ではbzImageファイルを伸長する特定のツールはない。しかし、カーネルソースコードのアーカイブ内にscripts/extract-ikconfigというbashベースの簡易なシェルスクリプトが存在する。このスクリプトは、引数に与えたイメージを伸長し、イメージからカーネルビルドコンフィグレーション(Kconfig)を抽出する。場合によっては、伸長したイメージを直接得るため、bzImageを改変することも可能である[12]。いくつかのディストリビューション、例として、RedHatとそのクローン(CentOSなど)にはカーネルのRPMパッケージに対応するvmlinuxファイルを持つkernel-debuginfoなるパッケージがあるかもしれない。概して、そのようなパッケージは/usr/lib/debug/lib/modules/`uname -r`/vmlinuxにインストールされる。

オブジェクトファイルフォーマット[編集]

以下は、x86-64アーキテクチャにおけるGentoo Linuxで稼働する、カーネル(バージョン2.6.29)の実行可能イメージからヘッダ情報を抽出したものである(GNU Binutilsパッケージに付属するreadelfコマンドは、ELF定義済みヘッダを出力するコマンドである[13])。

$ readelf -h vmlinux
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x1000000
  Start of program headers:          64 (bytes into file)
  Start of section headers:          13951312 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         5
  Size of section headers:           64 (bytes)
  Number of section headers:         45
  Section header string table index: 42

ちなみに、同じコマンドをbzImageに実行しても次のようなエラーが返されるだけである。

$ readelf -h arch/x86/boot/bzImage
readelf: Error: Unable to seek to 0xc031f2eb for section headers
readelf: Error: Not an ELF file - it has the wrong magic bytes at the start

bzImageは前述の通りELFヘッダを持たないrawバイナリである。

脚注[編集]

[ヘルプ]

注釈[編集]

  1. ^ アーキテクチャによっては、"Decompressing Linux..."の場合もある。

出典[編集]

  1. ^ カーネル・イメージとは - Linuxキーワード”. 日経ITpro. itpro.nikkeibp.co.jp (2008年2月18日). 2011年8月13日閲覧。
  2. ^ a b 2009年6月9日にリリースされた2.6.30からは、LZMABZIP2アルゴリズムによるカーネルイメージの圧縮がサポートされている[1]。また2010年2月24日にリリースされた2.6.33からは、Lempel–Ziv–Oberhumer(LZO)圧縮アルゴリズムのサポート(ChangeLog-2.6.33)、2.6.39からはx86アーキテクチャにおいて、LZMA2圧縮アルゴリズムのフリーな実装XZによる圧縮サポートが実装された(Decompressors: Add XZ decompressor module LWN.net)。
  3. ^ SPARCアーキテクチャ用 arch/sparc/boot/Makefile
  4. ^ i386アーキテクチャ用 arch/x86/boot/compressed/head_32.S
  5. ^ x86アーキテクチャ用 arch/x86/boot/compressed/misc.c
  6. ^ a b objcopy(1) – Linux User Commands Manual (en)
  7. ^ x86アーキテクチャ用 arch/x86/boot/compressed/Makefile
  8. ^ x86アーキテクチャ用 arch/x86/boot/header.S
  9. ^ GNU Binary Utilities: objcopy
  10. ^ Documentation/x86/boot.txt
  11. ^ init/main.c
  12. ^ vmlinuz を解凍するための 1f 8b 08 00”. apribase.net (2010年2月26日). 2011年8月13日閲覧。
  13. ^ readelf(1) – Linux User Commands Manual (en)

関連項目[編集]

外部リンク[編集]