I/O スケジューラ (その2)
前回は I/O ブロックレイヤの上位レベルの話をした。I/O ブロックレイヤでのデータ構造について調べ、実際の処理についてソースを追っていった。そして bio オブジェクトを request_queue に併合する処理がブロックデバイス毎に行える設計についての話をした。
今回は I/O ブロックレイヤと I/O スケジューラの関連を簡単に調べ、そもそも I/O スケジューラとは何ぞのもので、どんな処理をするものなのかという事を特に詳しく見てゆく。
もちろん、これについても今回一回の話で I/O スケジューラの何たるかについて全てを理解できるわけではないので、複数回に分けて話をするつもりだ。
まずは、前回の続き ( __make_generic_request() で、request_queue オブジェクトに bio オブジェクトを併合する処理 ( make_request_fn メソッド) の続き) について見てゆく。
HDD を含む blk_init_queue() を呼び出す多くのを含むドライバでは request_queue オブジェクトの make_request_fn メソッドによって __make_request() が実行される。
__make_request() では、何度も言っているように request_queue オブジェクトの最適な場所に bio オブジェクトの併合を行う。その「最適」の判断を行うのが I/O スケジューラになる。
以下では、__make_request() の中身の処理について少し詳しく見てみる。
まずは bio_sectors によって引数で渡された bio オブジェクトが対象としているブロックデバイスのセクタ番号を取得する。次に、blk_queue_bounce() を呼び出し、バウンスバッファが必要かどうかを判定する。
教科書的な話になるが。一般的にアーキテクチャでは CPU に接続された各コンポーネント (メモリや (PCI 等の) デバイスコントローラなど) は共通のバスを介してデータ通信を行っている。
つまり、メモリに乗っかっているデータを CPU に転送する場合も、デバイスに転送する場合も、同じラインにデータが乗っかる。しかし、デバイスないしデバイスコントローラによっては、高位のアドレス領域のアドレスラインが接続されていないケースがある。
これに対応するために、必要に応じて bio のバッファ領域が高位アドレスにあるバッファは低位アドレス (仮想メモリ上のカーネル空間の上位 3/4 ) 領域のバッファを介して転送される。
GNU/Linux では、この低位アドレス領域のバッファをバウンスバッファと読んでいる。
そして __make_request() では次に elv_merge() を呼び出し、汎用 I/O スケジューラの処理に bio オブジェクトをマージする request オブジェクトの問い合わせを行う。
このように。I/O ブロックレイヤの下位レベルに I/O スケジューラの処理が存在している事がわかる。そして、elv_merge() の処理は汎用の I/O スケジューラの処理で、この内部では各 I/O スケジューラのメソッドを呼び出す。GNU/Linux では、elv_merge() といった処理を含めたここまでの処理を I/O ブロックレイヤと呼んでいる。
では I/O ブロックレイヤと I/O スケジューラの役割はそれぞれ何か。
I/O ブロックレイヤでは、上位レイヤに対して request_queue オブジェクトで表されるキューというデータ構造を提供し、request で表される I/O 要求のソート、キューイング、そしてファイルシステムによらない I/O 要求処理を担当する。
それでは狭義の I/O スケジューラとは何で、どんな処理を行うものなのか?
多くを語らない GNU/Linux のマニュアル [1] によると、I/O スケジューラはファイルシステムによる I/O 要求処理とそれらの遅延を担当するらしい。
ちなみにここで言っている "遅延" とは、キャッシュのことでは無い。カーネルの I/O 要求 (特に write 要求) の場合、特定の場所 (ブロックデバイスの物理アドレス) X に対する I/O 要求が到来してから近い将来、アドレス空間的に X に近い場所に対する I/O 要求がなされる可能性が高い事 (ファイルシステム I/O に空間的な局所性の特徴があること) が経験的にわかっている。
そこで GNU/Linux の多くの I/O スケジューラは、非同期の write 要求処理が到来した場合に遅延し、積極的に要求の統合を行ってデバイスに対して発行する I/O の絶対数を減らす努力を行っている。
また I/O スケジューラ自体は、I/O ブロックレイヤと異なりカーネルモジュールであり、動的に変更が可能である。
カーネルモジュールである I/O スケジューラは GNU/Linux におけるひとつのクラスと考える事が出来る。次回は、I/O スケジューラの API について一通りみた後、CFQ I/O スケジューラにおける各 API の実装について詳しく見てゆく。
最終的に、このテーマについて一連の投稿を終えた段階で、I/O スケジューラとは名んぞで、どんなモノか。そして、具体的にどんな処理を行うものなのかという事を一通り理解できている所まで持っていきたい。
まとめとして、今回学習した事は次の 2 つ。
(1) I/O の処理の流れ (make_request_fn メソッドが呼ばれてから elv_merge() 処理が呼ばれるまで)
(2) I/O ブロックレイヤの役割と I/O スケジューラの役割
[1] Documentation/block/biodoc.txt
- Category(s)
- 学習経過
- The URL to Trackback this entry is:
- http://dev.ariel-networks.com/Members/ohyama/i-o-30b930b130e530fc30e9-305d306e2/tbping