ファイルシステム(その4)
前回、ext2 マウント時における、スーパーブロックオブジェクトと、各ブロックグループディスクリプタのキャッシュ処理について見てきた。
マウント処理の全体のうち、局所的な内容であったが、他の処理については、後々紹介してゆく(と思う)。
今回は、ファイルシステムによるファイル操作の実装について調べてゆく。尚、ファイルアクセスに付随する周辺の仕組みについて多く触れるが、ここでは、ファイルアクセス。特に、ブロックアドレスのアドレス変換に焦点を絞って述べてゆく。ここでは、ディスクからのデータ読み込みを例に話を進める。
ファイルシステムが提供するファイルの中枢を成すデータ構造は、inode と呼ばれ、ファイル 1つ に対して 1つ の inode が存在している。
ファイルのデータはディスク上に存在しているが、ユーザー操作によるファイルの読み書きは、ファイルデータをキャッシュしたページキャッシュに対して行われる( cf. ページキャッシュ(その2) )。
ファイルに対して読み書きを行う際、キャッシュを参照するが、キャッシュツリーに対応するページディスクリプタが存在しなかった場合、カーネルは page_cache_alloc_cold() を呼び出し、ページをキャッシュに追加、そして add_to_page_cache_lru() を呼び出し、キャッシュページを LRUリスト に登録する。
又ページが存在している場合において、ページが無効である(PageUptodate フラグが降りている)場合、有効なデータが含まれていないので、ディスクからデータを取得する。
具体的には、address_space のページ操作メソッドが登録された構造体(address_space_operations 型の a_ops メンバ) の readpage メソッドを呼び出す。
ファイルシステムに ext2 を使っている場合、ここでは、ext2_readpage() が呼ばれる。
ここでは、ページディスクリプタと、get_block のコールバック関数 ext2_get_block を引数に渡して、mpage_readpage を呼び出す。
get_block は、mpage_readpage() 関数内で呼び出され、ファイルポインタ(プロセスが開いているファイル内におけるオフセット)に対するディスクの実ブロック番号を返す、ファイルシステム固有の関数である。つまりここでは、ブロック番号のアドレス変換を低級ファイルシステムに行わせ、結果を受けて、VFS が I/O を行うようになっている。
さて、ext2 におけるディスクの使い方については、"ファイルシステム(その1)"で述べた。又、アドレス変換の仕組みについては、"ファイルシステム(その2)"で述べた。
ここでは、ext2 における get_block() 関数である ext2_get_block() から、実際にアドレス変換を行う実装をみてゆく。
まず、ext2_block_to_path() を呼び出す。
ここでは、ファイルポインタに対する ディスク inode の三段の間接参照における、それぞれの論理ブロック番号を整数型の配列(offsets[4])で表現する。つまり、直接参照の場合、2番目の要素に論理ブロック番号が格納される。二段参照の場合、2番目の要素に、13 (二段参照の際の i_block メンバのオフセット) が格納され、3番目に二段目からのオフセットが格納される。この場合、配列の先頭要素には、EXT2_IND_BLOCK マクロで表される値が格納される。三段参照の場合も同様に論理ブロック番号を求める。
ここで2つのデータ構造を紹介する。一つは、ext2_inode_info 構造体。
これは、ディスク inode に対応する構造体であり、VFS inode (以下、単に inode と呼ぶ )オブジェクトを組み込んでいる。inode からは、EXT2_I() マクロによって ext2_inode_info のアドレスを取得できる。
そしてもう一つが、Indirect 型のデータ構造である。これは、実ブロック番号と実ブロック番号を参照する変数のアドレス、そしてバッファヘッドから成るデータ構造で、それぞれ、"key", "p" そして "bh" メンバがそれに対応する。これは、後に説明する処理で使われる。
さて。ここまでの処理で、ファイルポインタから、ディスク inode のデータ構造における、データブロック位置を求めた。
次に、ext2_block_to_path() によって、ファイルポインタに対応するディスク inode の間接参照の回数を求めることが出来た。depth で返されたこの値を引数で渡し、ext2_get_branch() 関数を呼び出し、論理ブロック番号から実ブロック番号を取得する。
ここで add_chain() 関数に、ディスク inode のデータブロックの配列 i_data と ext2_block_to_path() で設定した offsets の和を引数で渡し、Indirect型 変数 chain の "p"(実ブロック番号を格納したエントリのアドレス), "key"(実ブロック番号) を設定する。初回の呼び出しでは、第二引数に NULL を渡し、chain オブジェクトのバッファヘッドを NULL に設定する。
ここまでの処理で、直接参照における実ブロック番号が取得できた( chain オブジェクトの key メンバに格納されている)。そして、sb_bread() 関数を呼び出し、VFSスーパーブロックオブジェクト(super_block型)の s_bdev メンバから参照できるブロックデバイスから、第二引数で渡される実ブロック番号のブロックバッファを参照(無ければ作成)し、バッファヘッドを返している。
ブロックバッファが作成できたので、バッファページが作成され、キャッシュにページが挿入され、VFS から読み書きを行える。
尚、作成されたキャッシュのディスクへの書き込みについては、"ページキャッシュ(その3)"で述べている。
ここまでが、ext2 ファイルシステムを巻き込んだ、ファイルからのデータ読み込みの処理の大まかな流れである。
- Category(s)
- 学習経過
- The URL to Trackback this entry is:
- http://dev.ariel-networks.com/Members/ohyama/30d530a130a430eb30b730b930c630e0-305d306e4/tbping