ファイルシステム(その3)
前回、前々回 で、ファイルシステムの概観を学習した。今回から、linux における ext2 ファイルシステムの実装について、調べてゆく。
今回は、スーパーブロックのデータ構造と、初期化処理の実装について調べてゆく。
カーネルは、ext2 パーティーションのスーパーブロックと、各ブロックグループディスクリプタのバッファヘッドを ext2_sb_info 構造体( include/linux/ext2_fs_sb.h )のディスクリプタから参照する。
ext2_fill_super() 関数は、ページキャッシュを使って、ディスクファイルシステム上のスーパーブロックと各ブロックグループディスクリプタをキャッシュし、ext2_sb_info 構造体のオブジェクトの初期化を行う。このように、カーネルはページキャッシュを使って、ディスクデータのキャッシュを取得する。この関すは、ファイルシステムマウント時に呼び出される。
ブロックバッファは、ファイルシステムにおけるキャッシュ内容が格納される。その領域は、キャッシュページに含まれており、バッファヘッドの b_data メンバによってこれを参照する。又、ブロックバッファは、ブロック型デバイスと、実ブロック番号によって識別され、これ (ページキャッシュ機構) が linux I/Oシステムがファイルシステムより上位レイヤのシステムに対して、ディスク走査を抽象化している(と思っている)。
バッファヘッドは、k_mem_cache_s スラブキャッシュを持っており、alloc_buffer_head() 関数でバッファヘッド領域を取得する。又、バッファページは、grow_buffers() 関数によって確保される。grow_buffers() では、引数で渡されたブロックバッファのアドレス空間オブジェクトのページキャッシュにページを追加する。
処理の流れを簡単に追うと、__bread() 関数から、__getblk() 関数を呼び出す。この内部では、__getblk_slow() 関数を呼び出し、引数で指定したブロック型デバイスディスクリプタと、実ブロック番号に対応するページキャッシュが見付からない場合、grow_buffers() 関数を呼び出し、キャッシュを作成する。この内部では、find_or_create_page() 関数を呼び出し、alloc_page() マクロによって、キャッシュページを取得し、ブロックデバイスのアドレス空間オブジェクトのページキャッシュにページを格納し、alloc_page_buffers() 関数を呼び出し、実ブロックに対応するバッファヘッドを作成する。
尚、grow_buffers() 関数は、作成したバッファヘッドを返さないが、ページキャッシュのページディスクリプタの private メンバによってバッファヘッダが参照される。これは、ページとバッファヘッドが作成された後、link_dev_buffers() によって行われる。
又、ここで行われた処理は、ページキャッシュの領域を確保しただけであり、__getblk() 関数を終了した時点では、ディスクからのデータはキャッシュに読み込まれていない。
この処理を __bread_slow() 関数が行う。ここでは、今しがた作成した バッファヘッド とI/Oの方向(READ/WRITE) を指定して、submit_sh() 関数を呼び出し、処理を I/O システムに依託する。
カーネルが参照するページキャッシュは、ブロック型デバイスディスクリプタ (block_device型) の bd_inode メンバが参照する inode が持つアドレス空間オブジェクトの基数ツリーになる。これらの初期化処理は呼び出し側の関数が行い、ブロック型デバイスディスクリプタへの参照は、ここ (ext2_fill_super() 関数) では、VFSスーパーブロックオブジェクト(super_block型) の s_bdev メンバを通して行う。
さて。話を戻す。ここでの大きな仕事は2つ。1つは、スーパーブロックのページキャッシュを作成する事、もう1つは、書くブロックグループディスクリプタのページキャッシュを作成する事。
get_sb_block() 関数は ext2_fill_super() 関数の第2引数の(謎)のデータ(恐らく、実ブロック番号を文字列で表したデータ)を受け取り、スーパーブロックの実ブロック番号を返す。sb_bread() 関数では、先ほど解説した __bread() 関数を呼び出し、スーパーブロックのページキャッシュを取得し、ext2_sb_info 型オブジェクトの s_ex メンバにバッファヘッドを格納し、ブロックバッファを s_es メンバに格納する。
カーネルは、バッファヘッドの b_data から、キャッシュページを直接参照し、ローカル変数 es (ext2_super_block型) に代入し、パーティションのスーパーブロックの値を操作する。
そして、s_mount_opt メンバにマウントオプションを設定した後、スーパーブロックからファイルに関する情報 (ディスク上のinode 構造体の大きさ、最初の未定inode番号、ブロックグループに対するブロック数など) を取得する。
次に、各ブロックグループディスクリプタのページキャッシュを取得する。
前々回に、ブロックサイズによってブロックグループのサイズ(ブロック数)が決定される事から、パーティションのブロックグループの数を算出できる事を述べた。ここでそれを利用している。
db_count 変数に、ブロック単位で表したファイルシステムの大きさと先に初期化設定した s_blocks_per_group (1つのブロックグループ当りのブロック数)の商を格納し、kmalloc() 関数を呼び出し、汎用キャッシュから各ブロックグループのキャッシュに対応するバッファヘッドのメモリ領域を取得する。
そして、ディスクパーティション上の各ブロックグループディスクリプタをキャッシュする為に、sb_bread() 関数を呼び出す。各ブロックグループの実ブロック番号は、descriptor_loc() 関数によって取得する。ブロックグループディスクリプタは、各ブロックグループのスーパーブロックのコピーの後に割り当てられる。よって、これまでに読み込んだ回数によって、各ブロックグループのブロックグループディスクリプタの実ブロック番号が特定できる。
そして、ext2 の固定情報(ext2_sops (操作メソッド)など)を設定して、処理を終了する。
ここまでが、ext2_sb_info 型オブジェクトの初期化処理である。
- Category(s)
- 学習経過
- The URL to Trackback this entry is:
- http://dev.ariel-networks.com/Members/ohyama/30d530a130a430eb30b730b930c630e0-305d306e3/tbping