ルートファイルシステムが読み込まれる前に、デバイスに対する処理を挟む (その 2)
前回は永続キャッシュされたデータをブート時に取り出すという話をした。
ディスク I/O を何らかのストレージで遅延させた場合、システムが突然クラッシュした際にルートファイルシステムが存在するデバイスとディスク I/O をキャッシュしている間でのデータの不整合が生じてしまう。
そこでブートの際、ルートファイルシステムがマウントされる前に、キャッシュデバイスのデータをルートファイルシステムが存在するデバイスに同期させる処理を挟む。という内容の話をした。
今回はそれを実現する為の具体的な方法について述べてゆく。
前回の話で ramdisk がメモリに乗っかるまでの処理を確認した。
この時点で各ドライバの実行コードがオンメモリにあるので、これらを叩く処理を行えばよいという結論だったが。後にカーネルが行う初期化処理を自前のコードが行うというのは非常にナンセンスなので、ドライバの初期化が行われて後にキャッシュデータを HDD に同期させる処理 (以下、ライトバック処理) を実行させることにする。
という事で前回の内容と若干被るが start_kernel() から処理を簡単に追ってゆく。
プロテクトモードに入った直後に呼び出される start_kernel() では irq の再設定やページテーブルの再設定といったお決まりの処理の後、メモリアロケータ、スケジューラ、I/O サブシステムのデータ構造の初期化を行う。
I/O サブシステムの初期化と言っているのは、kobject の初期化であり、この時点ではまだ HDD やキャッシュデバイスの gendisk オブジェクトやデバイスのリクエストキューは生成されていない。
そして、start_kernel() の最後に 2 つのカーネルスレッド (kernel_init と kthreadd) が起動される。
kernel_init はこれ自体はほとんど何の処理も行わないですぐに init プロセスの実行を行う。kthreadd はデバイスの初期化処理とは無関係だが、カーネルスレッド生成に伴うとても重要な処理を行うカーネルスレッドである。これについては、後述する。
さて。kernel_init() では、ロードされた ramdisk の /init プログラムを実行するために run_init_process() を呼び出す。これが無ければ、
/sbin/init
/etc/init
/bin/init
/bin/sh
の実行を試行する。run_init_process() の内部では、kernel_execve() を呼び出し、引数で与えられたプログラムに対して execve サービスルーチンを実行する。なので /init 以下、いづれかのプログラムが実行された時点で kernel_init に処理が返ることはない。
init プロセスが起動した段階では、リクエストキューなどのデバイス関連のデータ構造はまだ初期化されていない。ここで udev の初期化スクリプトを実行してデバイスの probe が行われる。そして、各種デバイスの probe 処理で gendisk やリクエストキューなどの初期化が行われるようになる。
この段階ではデバイスファイルが作成されているので、ユーザプロセスからもデバイスに対する I/O が簡単に行える。あとは自前のライトバック処理で、ディスク I/O のキャッシュと同様に、カーネル内部で bio オブジェクトを生成し、直接リクエストキューに送ればキャッシュデバイスと HDD との間でのデータの同期が行える。
このようにして、ディスク I/O をキャッシュし、キャッシュが汚れた状態でシステムが停止しても、再起動後にキャッシュ内容が HDD のファイルシステムに反映させる事ができた。これによって、キャッシュシステム導入後のシステムの可用性を HDD 単体で動作させた状態と同じレベルまで上げる事ができる。
- Category(s)
- 学習経過
- The URL to Trackback this entry is:
- http://dev.ariel-networks.com/Members/ohyama/30eb30fc30c830d530a130a430eb30b730b930c630e08aad307f8fbc307e308c308b524d306b300130a430b9306b5bfe3059308b51e674063092631f3080-305d306e-2/tbping