VFSの構造・動作。
UNIXのVFS実装(最前線UNIXのカーネルより)
代表的なオブジェクト
vノード
struct vnode { u_short v_flag; /* V_ROOTなど */ u_short v_count; /* 参照カウントなど */ struct vfs *vfsmountedhere; /* マウントポイント用 */ struct vnodeops *v_op; /* vノード操作のベクタ */ struct vfs *vfsp; /* vノードが属するファイルシステム */ struct stdata *v_stream; /* 関連ストリームへのポインタ */ struct page *v_page; /* 常駐ページリスト */ enum vtype v_type; /* ファイルの種類 */ dev_t v_rdev; /* デバイスファイル用のデバイスID */ caddr_t v_data; /* プライベートデータ構造へのポインタ */ … };
vノード操作のベクタ
struct vnodeops { int (*vop_open)(); int (*vop_close)(); int (*vop_read)(); … };
vfsオブジェクト
struct vfs { struct vfs *vfs_next; /* リスト中の次のVFS */ struct vfsops *vfs_op; /* 操作のベクタ */ struct vnode *vfs_vnodecovered; /* マウントされるvノード */ caddr_t vfs_data; /* プライベートデータ */ dev_t vfs_dev; /* デバイスID */ … };
vfs操作のベクタ
struct vfsops { int (*vfs_mount)(); int (*vfs_unmount)(); int (*vop_root)(); int (*vfs_statvfs)(); int (*vfs_sync)(); … };
仮想ファイルシステムスイッチテーブル
struct vfssw { char *vsw_name; /* ファイルシステムの名前 */ int (*vsw_init)(); /* 初期化ルーチンのアドレス */ struct vfsops vfsw_vfsops; /* このファイルシステム用のvfs操作ベクタ */ … } vfssw[];
オブジェクト間の静的構造
- 大域変数rootvfsから、nextによりvfsオブジェクトを辿ることができる。vfssw配列とvfsオブジェクトリストにより、カーネルはいつでも特定ファイルシステムを表すvfsオブジェクトを取得できる。
rootvfs -> vfsオブジェクト -> vfsオブジェクト -> …
- vfsオブジェクトからは、マウントポイントのvノードを辿ることができる。これにより、あるファイルシステムのvノードから親階層に向けて論理ツリーを辿る際にファイルシステムをまたがって遡ることができる。
vfsオブジェクト -> マウントポイントのvノード -> 所属するvfsオブジェクト -> …
- あるファイルシステムのルートにあたるvノードには、VROOTフラグあり
論理ツリーを遡る際に、現在のvノードがファイルシステムのルートかどうか識別する際に用いる。
- vfsオブジェクトは、ファイルシステム依存データへのポインタを持つ。
vfsオブジェクト -> vfs_data
- vfsオブジェクトは、ファイルシステム依存操作へのポインタを持つ。
vfsオブジェクト -> vfs_op
- vノードは、ファイルシステム依存データへのポインタを持つ。
vノード -> v_data
- vノードは、ファイルシステム依存操作へのポインタを持つ。
vノード -> v_op
オブジェクト間の相互作用
システムコール呼び出し時の動作
- open操作
- ユーザプロセスにより、openシステムコールが呼び出される。
- カーネルは、ファイル記述子を割り当てる。
- カーネルは、オープンファイルオブジェクトを割り当て、ファイル記述子テーブルにこの構造体へのポインタを保存する。
- カーネルは、lookuppn()によりパス名を解決し、オープンするファイルのvノードを得る。この際lookuppn()は、親ディレクトリのvノードも返す。(*1)
- カーネルは、呼び出し元が必要な権限を持っているかどうかをVOP_ACCESSにより確認する。
- カーネルは、ディレクトリのオープンや実行中ファイルへの書き込みが行われていないかを確認する。
- ファイルが存在していない場合、カーネルはO_CREATオプションを確認する。指定されていた場合、カーネルはVOP_CREATにより親ディレクトリ上にファイルを作成する。
指定されていなかった場合、カーネルはENOENTエラーを返す。
- read/write操作
- ユーザプロセスにより、read/writeシステムコールが呼び出される。
- カーネルは引数からuio構造体を作成する。
- カーネルは引数のファイル記述子からオープンファイルオブジェクトを探し、要求された操作が可能な型でopenされているかを確認する。
- カーネルは、オープンファイルオブジェクトから、vノードを取得する。
- カーネルはVOP_RWLOCKでvノードをロックする。
- カーネルは、VOP_READ/VOP_WRITEを呼び出す。
- I/Oが完了したら、カーネルはVOP_UNLOCKでロックを解除する。
- カーネルは、ユーザプロセスに読み書きしたバイト数を返す。
- mount操作
- ユーザプロセスにより、mountシステムコールが呼び出される。
- カーネルは、ファイルシステム非依存のlookuppn()を呼び出し、マウントポイントのvノードを取得する。
- カーネルは、取得したvノードが(ファイルではなく)ディレクトリであり、既に他のファイルシステムがマウントされていないかを確認する。
- カーネルはvfsswリストを検索し、マウント対象のvfsオブジェクトを取得する。
- カーネルは、vfs_op->vfs_initを実行する。これにより、以下の処理が行われる。
- rootvfs変数を始点とするvfsオブジェクトリストに新たなvfsオブジェクトが追加される。
- 追加されたオブジェクトのvfs_opにvfsswで指定されたvfsopsがセットされる。
- 追加されたオブジェクトのvfs_vnodeoveredにマウントポイントのvノードがセットされる。
- vfs_init操作が終了する。続いて、カーネルはVFS_MOUNTを実行する。これにより、ファイルシステムに依存した以下の処理が行われる。
- 操作の権限がチェックされる。
- ファイルシステム依存のデータオブジェクトが割り当てられ、ディスクからのデータを元に初期化される。
- 割り当てられたデータオブジェクトが、vfsオブジェクトのvfs_dataに保存される。
- ファイルシステムのルートディレクトリにアクセスし、そのvノードがメモリ中に初期化される。(*4)