目次


Kexecを使ってLinuxの起動を早める

起動時間高速化のためにブートローダーを省略する

Comments

コンピューター・システムがより早く、より高性能になりながら改善が追いつかずにいる領域がシステムのリブート時間です。実際の所、プロセッサーのスピードやメモリ・サイズ、リソース機能などの面でシステムがより進歩し、複雑になるにつれて、リブート時間はむしろ長くなっています。ブート時間が長いのは誰にとっても苛立たしいものですが、特に実稼働中のシステムではブート時間が長ければそれだけ稼働時間が短くなることを意味するので、影響は致命的になります。ユーザーがシステムを使える時間に影響するだけではなく、毎日マシンを何度もリブートせざるを得ないカーネルやシステム・ソフトウェアの開発者にとって、リブート時間が長いのは大きな障害になります。

接続台数の少ないSCSIバスや、ECCチェックを行う物理メモリが多数ある場合には、リブート時間は特に長くなります。いろいろなテスト結果によると、リブートのプロセスで最も時間がかかるのが、システムに付加されたデバイスを認識・初期化するファームウェアの段階です(詳細については参考文献を見てください)。当然ながらリブート時間短縮のための努力はこの段階を対象にしています。そうした努力の一つの中から、x86プラットフォーム用Linuxで使用可能な機能であるkexecが開発されました。Kexecを使うとファームウェアやブートローダーの段階を経ずに、リブートして別のカーネルを起動することができるようになります。ブート・シーケンスで一番時間がかかる部分を省略するので、リブート時間は劇的に短くなるのです。

Linuxでのブートの概要

Kexecを理解するにはLinuxでのブートプロセスの知識が必須です。Linuxでのブートプロセスにはブートローダーの段階とカーネル段階という2つの段階があります。

ブートローダー段階の主な要素には、ハードウェア段階、ファームウェア段階、第1レベル・ブートローダー、第2レベル・ブートローダーがあります。ブートのプロセスはハードウェアの電源がオンされた時に始まります。一定の初期化が行われた後、制御はファームウェアに移ります。ファームウェアはアーキテクチャーによっては「BIOS」とも言われますが、システム上にある様々なデバイス、例えばメモリ・コントローラーやストレージ・デバイス、バス・ブリッジその他のデバイスを検出します。ファームウェアは設定に基づいて、マスター・ブート・レコードとして知られている最小限のブートローダーに制御を渡します。マスター・ブートレコードはディスク上にある場合もあれば、リムーバブル・メディアまたはネットワーク上にある場合もあります。制御をオペレーティング・システムに渡すという実際の仕事は第2段階のブートローダー(普通は単に「ブートローダー」と言われます)が行います。このブートローダーによってユーザーは、ロードするカーネルを選択することができます。またブートローダーは選択したカーネルや関連のパラメーターをメモリにロードし、カーネルを初期化し、必要な環境を設定し、そして最後にカーネルを「実行」します。

次はカーネル段階で、この時にカーネルが制御を握ります。カーネルは必要なデータ構造を設定し、システム上にあるデバイスを検出し、必要なデバイス・ドライバーをロードし、デバイスを初期化します。最後の段階はユーザー・レベルの初期化を行います。この段階ではカーネルはファイルシステムの整合性をチェックし、ファイルシステムをマウントし、スワップ・パーティション(またはスワップ・ファイル)を設定し、システム・サービスを開始し、システム端末を設定し、その他種種雑多な設定を行います。

システム・リブートの期間中にはブートローダー段階に先だって、それまで実行していたシステムのシャットダウンがあります。これには実行中のプロセスの終了、キャッシュ・バッファーからディスクへの書き戻し、ファイルシステムのアンマウント、またハードウェア・リセットの実行などがあります。Linuxのブート・プロセスや、ブートに関連した概念の素晴らしい説明を参考文献に挙げてあります。

Kexecの概要

KexecはLinuxカーネルに対するパッチで、これを使うと現在実行中のカーネルから別のカーネルを直接ブートして移ることができるようになります。上で説明したブート・シーケンスを使って説明すると、kexecはブートローダー段階全体(第1段階)を省略し、新たにブートしたいカーネルに直接飛び込むのです。ハードウェア・リセットもなくファームウェア操作もなく、ブートローダーは全く関係しません。ブート・シーケンスで一番弱いリンク、つまりファームウェアを完全に回避しています。これによる大きな利点は、システム・リブートが非常に早くなることです。企業クラスのシステムでは、kexecによってリブート関連のシステムダウン時間を劇的に減少させることができます。カーネルやシステム・ソフトウェアの開発者にとっては、kexecを使うことによって時間のかかるファームウェア段階を毎回実行する必要がなくなり、開発やテスト作業に際して、ごく短時間でシステムをリブートできるようになります。

kexecパッチはEric Biedermanの作品であり、このプロジェクトでは活発な開発が行われています(プロジェクトの詳細や貢献の仕方については参考文献を見てください)。

当然のことですが、この機能はオペレーティング・システムの繊細な部分を数多くいじるので、適切に動かすためには十分な注意が必要です。Kexecで一番難しい点は、新しくリブートされるべきカーネルは現在実行中のカーネルとメモリ中で同じ場所にある必要がある、という点です。メモリ中に存在しているカーネルを、そのカーネルがまだ実行している間に新しいカーネルで置き換えるのは非常に困難な課題です。もう一つ大きな問題はデバイスの状態です。ファームウェアは常にデバイスを既知の「正気の」状態に初期化(またはリセット)します。Kexecがファームウェア段階を回避するということは、デバイスの状態が信頼できないということを意味します。

この先の説明では、こうした困難をどのように克服し、新しいカーネルへの直接ブートをどのように実現しているかを説明します。注意して欲しいのですが、kexecは現在x86の32ビット・プラットフォームでしか使えません。他のプラットフォームに移植するための努力は続けられていますが、動作するバージョンはまだありません。ですから、これから先の説明での技術的な詳細はx86プラットフォームに限定したものです。

Kexecを使う

Kexecには2つの要素があります。一つは「kexecツール」として知られるユーザー空間要素であり、もう一つが実際のカーネル・パッチです。この2つの要素が、kexecが行う2つの主な操作、つまり新しいカーネルをメモリにロードしてくることと、そのカーネルにリブートすること、という操作を行います。Kexecが動作するカーネルの入手は単純です。単にkexecツール・パッケージとカーネル専用のパッチをダウンロードし(参考文献のリンクを参照)、kexecツール・パッケージをビルドするとkexecツールが得られるので、カーナル・ツリーにカーネル専用のパッチを当ててリブートします。当然ですが、カーネルをビルドする時にはCONFIG_KEXECオプションを選んでおく必要があります。

上で説明したようにkexecを使うには2つの段階があり、(1)メモリ中にこれからリブートするべきカーネルをロードし、次に(2)それを実際にリブートします。カーネルをロードするための構文は次の通りです。

kexec -l <kernel-image> --append="<command-line-options>"

ここで<kernel-image>はこれからリブートしたいカーネルファイルであり、<command-line-options>には新しいカーネルに渡す必要のある、コマンドラインのパラメーターが含まれています。コマンドラインのオプションが間違っているとリブート中に問題を起こすので、新しくリブートするカーネルに対して確実に適切な値を渡すには/proc/cmdlineの内容を渡すのが最も安全です。

例えばリブートしたいカーネル・イメージが/boot/bzImageで、/proc/cmdlineの内容が「root=/dev/hda1」の場合、カーネルをロードするためのコマンドは次のようになります。

kexec -l /boot/bzImage -append="root=/dev/hda1"

こうしてから、ロードしたカーネルに移って実際にリブートするには単に次のようにタイプします。

kexec -e

システムは瞬時にリブートします。通常のリブート・プロセスとは異なり、kexecはリブート前にシステムをきれいにシャットダウンはしません。Kexecをリブートする前に全てのアプリケーションを停止し、ファイルシステムをアンマウントするのはユーザーの責任になります。

Kexecの魔術

Kexec開発で最大の困難は、Linuxカーネルがメモリ中の固定したアドレスで実行しているという事実に起因しています。これは新しいカーネルが、現在実行しているカーネルと同じ場所にある必要がある、ということを意味します。x86システムでは、カーネルは物理アドレス0x100000(仮想アドレスは0xc0000000で、PAGE_OFFSETとして知られています)にあります。古いカーネルを新しいカーネルで上書きする作業は次の3段階で行われます。

  1. 新しいカーネルをメモリにコピーする
  2. このカーネル・イメージを動的カーネル・メモリに移動する
  3. このイメージを実際に目的とする所にコピー(現在のカーネルを上書き)し、新しいカーネルを開始する

初めの2つの段階はカーネルの「ロード中」に行われます。ここでの最初の作業は、カーネル・イメージ・ファイルの内容を解釈することです。Kexecツールは(原理的に)どんなカーネルも(非Linuxであっても)ロードして、それをブートすることができるように作られています。現在では、elf32形式であればどんなカーネル・イメージもブートすることができます。このファイルは構文解析され、カーネルの「セグメント」はバッファーにロードされます。こうしたセグメントはコードの内容によってカテゴリー分けされます。たとえば、一般的に使われる「bzImage」カーネル・ファイル・フォーマットの場合では、典型的なセグメントとしては16ビット・カーネルコードに対するもの、32ビット・カーネルコードに対するもの、それにinit ramdiskコードに対するものです。こうしたセグメントの追跡に使用する構造はkexec_segmentとして知られており、非常に単純な構造をしています。

リスト1. kexec_segment構造
struct kexec_segment {
		 void *buf;
		 size_t bufsz;
		 void *mem;
		 size_t memsz;
};

この構造の、初めの2つの要素はユーザー空間バッファーとそのサイズを指しますが、その次の2つの要素はこのセグメントの最終的な行き先とそのサイズを表します。

カーネル・ファイルのフォーマット専用モジュールがユーザー・メモリにロードされると、このイメージはsys_kexecシステム・コールを使うことで動的カーネル・メモリに移されます。このシステム・コールは、ユーザー空間から渡された各セグメントに動的カーネル・ページを割り振り、そのセグメントをカーネル・ページにコピーします。

Kexecはまた、(reboot_code_bufferとして知られる)アセンブリ・コードの小さなスタブを保存するためにもカーネル・ページを割り振ります。このコード・スタブが実際の作業、つまり現在実行中のカーネルを次にリブートすべきカーネルで上書きし、そこにジャンプするための作業を行います。reboot_code_bufferは最終的な安住の地にとどまる唯一のバッファーです。言い換えると、reboot_code_bufferは最初にロードされた所と同じ所から実行されるのです。これを実現するためには、MMUが動作しているシステムでは、このコードを保持するページを識別マップしています。単純に言うと、これによって物理アドレスと仮想アドレスを同じにしてinit_mm(カーネルのページテーブル構造)にページテーブル・エントリーを作る、ということをするのです。これは、リブート操作中にこの小さなコードにアクセスするために必要になります。これについては後で説明します。

reboot_code_bufferに関する情報、様々なセグメントやその他の詳細は、kimage構造を使うことで保持されます。

リスト2. kimage構造
struct kimage {
        kimage_entry_t head;
        kimage_entry_t *entry;
        kimage_entry_t *last_entry;
        unsigned long destination;
        unsigned long offset;
        unsigned long start;
        struct page *reboot_code_pages;
        unsigned long nr_segments;
        struct kexec_segment segment[KEXEC_SEGMENT_MAX+1];
        struct list_head dest_pages;
        struct list_head unuseable_pages;
};

この構造で最も重要な部分は当然ながら、イメージを保持しているカーネル・メモリ中のバッファーを指すsegment[KEXEC_SEGMENT_MAX+1]要素と、リブート中に使用するアセンブリ・スタブへのreboot_code_pagesポインターです。

カーネル・イメージのロードが済むと、システムはそこに移ってリブートできるようになります。新しいカーネルへの実際のリブート操作はkexec -eコマンドで開始されます。このコマンドは基本的に、カーネルを呼びsys_rebootシステム・コールを使ってリブートを実行しますが、これを特別なフラグ- LINUX_REBOOT_CMD_KEXECを使って実行するのです。

リブート・システム・コールはこの特別なフラグを見ると、machine_kexec()機能に制御を渡します。machine_kexec()が行う動作は非常にアーキテクチャーに特化したものとなっています。現在のx86実装では、この動作シーケンスは次の通りです。

  1. 識別マップされたreboot_code_bufferにアクセスするために、現在のプロセスのmm structから、カーネルのinit_mm構造を使うように切り替える。
  2. apicsを停止し、インタラプトを使用不可にする。
  3. アセンブリ・スタブ・コードを、カーネル・イメージのロード中に割り振ったreboot_code_bufferにコピーする。アセンブリ・コードはrelocate_new_kernelルーチンにあります。
  4. 全てのセグメント・レジスターにカーネルデータ・セグメント値(__KERNEL_DS)をロードし、GDTとIDTを無効化する。
  5. reboot_code_bufferにあるコードにジャンプし、新しいカーネルに重要情報をパラメーターとして渡す。こうした情報としては、カーネル・イメージの元アドレス/行き先アドレスを含むインダイレクトページ、新しいカーネルの開始アドレス、reboot_code_bufferページのアドレス、システムで物理アドレスエクステンション(physical address extension: PAE)が使用可能になっているかどうかを示すフラグ、などがあります。

アセンブリ・スタブ・コードは次のような操作を行います。

  • スタックから引数を読み込んでレジスターに保存し、インタラプトを使用不可にする。
  • 引数として渡された自分自身のページのアドレスを使用して、そのページの最後にスタックを設定する。
  • 新しいカーネル・イメージの開始アドレスをそのスタックに保存し、スタブ・コードから戻ると、システムが自動的に新しいカーネル・イメージの所に行くようにする。
  • cr0レジスターの適当なビットを設定することにより、ページングを使用不可にする。
  • ページ・ディレクトリのベース・レジスタ、cr4をゼロにリセットする。
  • Translation Lookaside Buffers (TLBs)の内容をクリアする。
  • カーネル・イメージ・ページのすべてを最終的な行き先ページにコピーする。
  • 再度TLBの内容をクリアする。
  • 全てのレジスターをゼロにリセットする。ただしスタック・ポインター・レジスターespは、新しいカーネルの開始アドレスを含むスタックを指しているので除く。
  • スタブ・コードから「戻る」。これで自動的にシステムが新しいカーネルの所に行くことになります。

このシーケンスが完了すると新しいカーネルがシステムを制御するようになり、システムが正常にブートされていることになります。

Kexecの利点

Kexecの恩恵を最大限に享受するのは、可用性要求の高いシステムや何度もシステムをリブートする必要のある開発者でしょう。Kexecはシステム・リブートで一番時間のかかる部分、つまりファームウェア段階を省略するので、リブートは非常に早くなり、可用性も増します。

Kexecはまた、クラッシュ・ダンプ・ツールとして面白い使い方があります。Linux Kernel Crash Dumps (LKCD)プロジェクト(参考文献)ではkexecを使って別のダンプ・メカニズムを開発しています。システムがパニック状態にある時、またはユーザーによってダンプが開始されると、システムメモリのイメージは圧縮され、利用可能で空きのあるメモリ・ページに保存されます。次にkexecを使ってシステムを別のカーネルにリブートします。この新しいカーネルに対しては、ダンプがどこに保存されているかが知らされ、新カーネルはこのメモリ領域を誰も使わないようにします。最終的にこのメモリ・ダンプはディスク・パーティションに書き出されるか、またはネットワークを経由して他のマシンに書き出されます。

この設計で鍵となるのは、リブート中のファームウェア段階を避けることによって、LKCDはファームウェアが物理メモリの内容を消してしまうのを防ぐことができるという点です。クラッシュの状況でも、LKCDはメモリ・イメージを目的とする場所に書き出すために、信頼性の低いディスクやネットワーク・デバイス・ドライバーに頼る必要がありません。一旦リブートが実行され、システムが信頼できる状態になったら、このダンプは通常システムのデバイス・ドライバーを使って、目的とする所に書き出すことができるのです。

Kexecの将来方向

Kexecは現在x86 32ビットのプラットフォームでのみ使用可能です。例えばPPC 64やAMD 64等のような、他のアーキテクチャーによるプラットフォームでも使えることが望ましいでしょう。またシャットダウン・インターフェースとの統合を改善し、プロセスの終了やデバイスのシャットダウン、ファイルシステムのアンマウントが優雅にできるようになれば、平均的なユーザーにはより便利なものになります。

あなたもkexecの開発に貢献することができます。まずはテスト用のシステムでkexecを試してみてください。また「fastboot」メーリング・リストに参加することもできます。ここではこのプロジェクトに関して、技術的なあらゆる議論が行われています(リンクは参考文献)。


ダウンロード可能なリソース


関連トピック

  • Kexecに関する議論はfastboot mailing listで行われています。
  • Linuxでのブート・プロセスや一般的なブートの概念に関しての素晴らしい説明が、Andy Pfifferによる Reducing System Reboot Time with kexecや、Werner AlmesbergerによるBooting Linux: The History and the Future(PDF)、またTigran AivazianによるLinux Kernel 2.4 Internalsにありますので、ぜひ読んでみてください。
  • Kexec以前にも、bootimgとTwo Kernel Monteという、少なくとも2つの類似プロジェクトが行われています。bootimg (boot kernel image)は新しいカーネルのロードやリブートに関しては類似の機能を持っています。bootimgにもユーザー空間要素とそれに対するカーネル要素があります。bootimgはLinuxの2.4シリーズのカーネル用には入手可能ですが、2.6カーネルには移植されていません。
  • Erik HendriksによるTwo Kernel Monte(x86にLinuxをロードするLinux)は、LinuxをブートするLinuxの機能を提供する別のプロジェクトです。Two Kernel Monteは完全にカーネル・モジュールとして実装されており、新しいカーネルへのリブートをロードする、という仕事を一回で行います。bootimgと同様、Two Kernel Monteも2.6シリーズのカーネルには移植されていません。
  • Hariprasad NellitheerthaはLinux Kernel Crash Dumps (LKCD)プロジェクトのLinux Technology Centerで働いています。LKCDの最新機能の一つがkexecに基づくダンプ機構です。
  • Linuxのシステム・サービスを並列で開始するのもブート時間改善に効果のあるテクニックです。これについてさらに詳しくはBoot Linux faster(developerWorks, 2003年9月)を読んでみてください。
  • ブート可能なLinuxをいくつか同じマシンにインストールしておくことも、開発やテストのための工夫としては有益です。デュアル・ブートLinux(developerWorks, 2002年4月)では、同じdistroの複数インスタンスをブートするためにドライブのパーティションをどのように切り、どのようにシステム設定するかを説明しています。
  • ブートロードの基本をdeveloperWorksのチュートリアルGetting to know GRUB(2001年1月)で学んでください。
  • 同じくHariprasad NellitheerthaによるLinuxカーネル・デバッガー内部(developerWorks, 2003年6月)は、カーネルの実行状態の追跡や、メモリやデータ構造検査のためにLinuxに組み込まれているツールの紹介です。
  • Developer BookstoreのLinuxセクションではLinux関係の技術書が値引きされていますので、ぜひご購入下さい。
  • Developer BookstoreのLinuxセクションではLinux関係の技術書が値引きされていますので、ぜひご購入下さい。
  • developerWorks Subscriptionを利用して、最新のIBMツールやミドルウェアを使用したLinuxアプリケーションの開発・テストを行ってください。WebSphereやDB2、Lotus、Rational、Tivoli等のIBMソフトウェアと、これらを12ヶ月間使用できるライセンスを、想像されるよりもずっと安価に入手することができます。

コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Linux, Open source
ArticleID=228565
ArticleTitle=Kexecを使ってLinuxの起動を早める
publish-date=05042004