I/O スケジューラ (その1)
これまで低レベルファイルシステム (主に ext2) やらページキャッシュやらスワップ処理やらの学習を行ってきた。が。どれも汎用ブロックレイヤの上位の話である。実際の I/O 処理 (I/O スケジューラ) 周辺の話はしてこなかった (学習した記憶はあるのだが)。
I/O スケジューラの話題を中心に、何回かに分けて GNU/Linux の実装を学習してゆく。初回は、汎用ブロックレイヤのデータ構造と I/O 発行の概要、そして GNU/Linux のソースのアウトラインを見てゆく。
最初に、登場するデータ構造について見て行く。各データ構造の細かな役割について詳しく見ないで、どんなものなのかを簡単に知っておく。
まずは request_queue [include/linux/blkdev.h] 構造体について。こいつは各ブロックデバイスについて一つ用意されているデータ構造であり、この中にリクエストキューが入っている。
次が request [include/linux/blkdev.h] 構造体。こいつは汎用ブロックレイヤにおいて発行される I/O リクエストを表すデータ構造である。一個以上の bio 型オブジェクトから構成される。そして重要なポイントが、包含する bio オブジェクト群は I/O スケジューラによって動的に変更される事である。これが I/O スケジューラ本体の内部処理になる。これについては次回以降で詳しく見てゆく。
汎用ブロックレイヤの内部でも細かなレイヤ構造が存在する。上位の汎用ブロックレイヤからは、受け取った bio リクエストを request_queue に格納して I/O を発行するように下のレイヤ (I/O スケジューラ) に委託する。上述したデータ構造は、上位の汎用ブロックレイヤで登場するデータ構造である。上位と下位のレイヤの隔たりについては後述する。
次に、汎用ブロックレイヤの内部処理について詳しくみてゆく。なお、以下で述べる処理は上位部分の処理になる。I/O スケジューラ本体の処理については、次回以降で詳しくみてゆく。
submit_bio() によって発行された bio リクエストは __generic_make_request() によって処理される。
内部ではまず、渡されたリクエストがデバイスの容量を越えていないかというチェックを行い、bdev_get_queue() を呼び出して、リクエストを発行するブロックデバイスの request_queue オブジェクトを取得する。
つぎに blk_partition_remap() を実行する。ここでは、I/O リクエストが対象としている物理アドレスに対して、パーティションによるオフセットの補正を行っている。
ファイルシステムなどの上位レベルでは、ファイルを構成するブロックデータの物理アドレスは所属しているパーティションの先頭からオフセットで表される。しかし、このアドレスはブロックデバイスの絶対アドレスとは異なる。パーティションの物理アドレスのゼロ地点以前にはパーティションテーブルがあり、そして MBR が存在する。I/O を発行する場合にはその物理アドレスの補正をおこなってやる必要がある。blk_partition_remap() 内部では gendisk オブジェクトを取得して、当該 I/O が対象とする物理アドレスのゼロ地点の補正を行っている。
そして __generic_make_request() では最後に、request_queue オブジェクトの make_request_fn メソッドを呼び出して、呼び出し元に帰る。
先に述べた汎用ブロックレイヤの上位と下位の区切りがこの部分になる。
__generic_make_request() では request_queue オブジェクトの make_request_fn メソッドを呼び出し、submit_bio() から受け取った bio オブジェクトを request オブジェクトに併合して、ブロックデバイスに対して I/O を発行するよう I/O スケジューラに依頼する。
HDD を含めた多くの request_queue では make_request_fn メソッドの呼び出しによって __make_request() 関数を実行する。
データ構造にコールバックメソッドを据えておいて、汎用関数を呼び出す理由は何か? それは GNU/Linux がブロック I/O の発行形態の多様性を認めている為である。
GNU/Linux の I/O スケジューラはブート時のカーネルパラメータ elevator によって設定する事ができる。設定が行われない場合、デフォルトの I/O スケジューラ (CFQ) が設定される。
しかし、システムに搭載されている全てのデバイスに対して、共通の I/O スケジューラを用いる事が適切でない場合がある。例えば、HDD と他の特殊なブロックデバイスが同一システムに混成されている場合など。
このようなケースでは、特定のブロックデバイスに対する I/O リクエストは独自の I/O スケジューラによって処理されるべき可能性がある。これに対応するために、デバイス固有の I/O スケジューラを持てるように make_request_fn メソッドによって、処理を切り離している。
まとめとして、今回学習した事は次の 3 つ。
(1) request_queue と request 構造体
(2) I/O の処理の流れ (request_queue に bio が入れられるまで)
(3) GNU/Linux のブロック I/O の多様化設計
次回は、汎用ブロックレイヤの下位 (I/O スケジューラ本体とその周辺) の処理について詳しく見て行く。
- Category(s)
- 学習経過
- The URL to Trackback this entry is:
- http://dev.ariel-networks.com/Members/ohyama/i-o-30b930b130e530fc30e9-305d306e1/tbping