writeシステムコールは、vfs sys_write関数にて、ファイルディスクリプタに対応するファイル構造体を見つけ(fget関数)、ファイル構造体に登録されているwriteオペレーションを呼び出す。
ext2ファイルシステムの場合、 通常ファイルではgeneric_file_write関数が呼び出される。(v2.4からページキャッシュ経由の処理に書き換えられた)ディレクトリ、シンボリックリンクファイルの場合、このオペレーションは無登録であるため、sys_write処理はエラー終了する。
I/O要求が丁度ページ単位であるわけではないので、generic_file_write関数は、I/Oの先頭と最後のページをまずキャッシュ上に読み込み、その後I/O要求のあったデータを上書きしてやる必要がある。
generic_file_write(ファイル構造体、ユーザ領域, ファイルポインタ)
if(アペンドモードなら) ファイルポインタをファイルエンドにする
while(書き込むデータがある間) {
ページキャッシュを確保する(__grab_cache_page関数)
(もしキャッシュ上になければ新規確保し、登録する)
ページの書き込み準備をする(iノードのprepare_writeオペレーションを呼び出す)
(ext2の場合は、block_prepare_writeが呼び出される。
必要があればディスクからデータを読み込んだり、
バッファのクリアを行う)
ユーザ領域のデータをバッファに読み込む(copy_from_user関数)
ページの書き込み要求を出す(iノードのcommit_writeオペレーションを呼び出す)
(ext2の場合は、generic_commit_writeが呼び出される。
dirtyなバッファの遅延書き込み要求を出すのみ)
ページキャッシュの解放(page_cache_release関数)
}
ページキャッシュの章でも説明したように、block_prepare_write関数は、ページ書き込みのための準備をする関数である。generic_commit_write関数を呼ぶ前に呼び出しておく必要がある。
block_prepare_write(iノード、I/O領域、読み込むページ, getblock関数)
ページにまだバッファヘッドが割り付けられていなければ、
バッファヘッドを確保しページとリンク(create_empty_buffer関数)
for (バッファのある間) {
I/O完了コールバック関数としては、end_buffer_io_syncを登録。
if(バッファがブロックと対応付けられていない) {
getblock関数(ext2では、ext2_getblock関数)を利用し、
inodeとファイルオフセットからブロック番号を求める。
if (新規確保したバッファなら) {
もしバッファキャッシュにキャッシュが残っていれば
そいつの解放を行う(unmap_underlying_metadata関数)
0クリアする。
}
if (バッファがまだ有効でないなら)
I/O要求の発行(ll_rw_block関数)
}
}
I/O完了を待ち合わせる(wait_on_buffer関数)
generic_commit_write関数は、ページの遅延書き込み要求を行う関数である。実際にはバッファキャッシュのフラッシュデーモン類に書き込みI/Oを委ねる。
generic_commit_write(iノード、I/O領域、読み込むページ)
for (ページに対応づけられたバッファ群に対し) {
バッファを有効状態(BH_Uptodate)にし、
かつバッファをDirtyな状態にする(__makr_dirty関数)
}
Dirtyなバッファが多いようならkflushデーモンを起動する(balance_dirty関数)
ページを有効にする(SetPageUptodate関数)
問題点
(NIS)HirokazuTakahashi
2000年12月09日 (土) 23時55分06秒 JST1