このページは、ZFS ファイルシステムに関連するソースコードの概要を説明します。ZFS の紹介を目的とはしていません。ファイルシステムアーキテクチャーの一般的意味はもちろん、よく使用される用語や定義をすでにある程度理解していることを前提としています。
Sun では、ZFS が次の 3 つのコンポーネントから構成されていると通常説明しています。ZPL (ZFS POSIX Layer)、DMU (Data Management Unit)、および SPA (Storage Pool Allocator) の 3 つのコンポーネントです。この説明はスライドウェア用の例としては役に立つのですが、実際の話はもう少し複雑です。次のイメージに、もう少し詳しく概要を示します。領域の 1 つをクリックすると、詳細とソースコードへのリンクが表示されます。
この画像では前述の 3 つの基本レイヤーがあるのは同じですが、各レイヤー内の相当数の要素も記載されています。また、管理パス、すなわち zfs(1M) と zpool(1M) だけでなく、zvol コンシューマも記載されています。これらのサブシステムの概要については後述します。この概要は、すべての仕組みを完全に網羅したものではありません。詰まるところ、ソースコードこそが最終的なドキュメントになります。ソースコードをわかりやすく記載し、十分なコメントを付けたつもりです。そうなっていない箇所を発見された場合は、いつでも ZFS ディスカッションフォーラムに投稿してください。
次に、POSIX ファイルシステム API を介してもっぱら ZFS と対話する基本アプリケーションを示します。事実上すべてのアプリケーションがこのカテゴリに分類されます。システムコールが、汎用的な OpenSolaris VFS レイヤーを経由して ZPL に渡されます。
ZFS は、「エミュレートされるボリューム」を作成できます。このボリュームはストレージプール内のストレージに基づいていますが、通常のデバイスとして /dev の下にあります。これはよく使用されるユースケースではありませんが、この機能が役に立つケースが少数ですがあります。これらのデバイスと直接対話する少数のアプリケーションがありますが、もっとも頻繁に使用するコンシューマはデバイスの上位層のターゲットドライバまたはカーネルファイルシステムです。
Solaris はビルド 28 から Web ベースの ZFS GUI とともに出荷されます。ZFS GUI は現時点では OpenSolaris には同梱されていませんが、JNI の上位層の Java ベース GUI の一例になります。
管理コンシューマになるアプリケーションとは、ZFS のファイルシステムやストレージプールを操作するアプリケーションで、この操作にはプロパティーやデータセット階層の調査が含まれます。zoneadm、zoneadmd、fstyp などの散在する例外もありますが、主なアプリケーションは zpool(1M) と zfs(1M) の 2 つです。
zpool(1M)
このコマンドは ZFS ストレージプールを作成および管理するのに使用されます。このコマンドの主目的はコマンド行入力の構文を解析して変換し、libzfs 呼び出しに渡して途中で発生するすべてのエラーを処理することです。このコマンドのソースは usr/src/cmd/zpool にあります。次のファイルが含まれています。
zfs(1M)
このコマンドは ZFS ファイルシステムの作成および管理に使用されます。zpool(1M) と同様に、目的はコマンド行の引数を解析して処理結果を libzfs に渡すことだけです。このコマンドのソースは usr/src/cmd/zfs にあります。そこには次のファイルが含まれています。
このライブラリは libzfs の Java インタフェースを提供します。これは現在非公開インタフェースで、libzfs の GUI 専用に作成されたものです。したがって、主な目的は操作をビジュアルに行うことです。大部分のアクションは CLI を介して行われるからです。このライブラリのソースは usr/src/lib/libzfs_jni にあります。
これは、管理アプリケーションが ZFS カーネルモジュールと対話するための主要インタフェースです。このライブラリは、ストレージプールおよびファイルシステムをアクセスおよび操作するための統合されたオブジェクトベースの機構を提供します。カーネルとの通信を行う背後の機構として、ioctl(2) による /dev/zfs の呼び出しが使用されます。このライブラリのソースは usr/src/lib/libzfs にあります。次のファイルが含まれています。
ZPL は ZFS ファイルシステムと対話するための主要インタフェースです。これは DMU の上位に位置する比較的薄い層で、ファイルおよびディレクトリからなる抽象化したファイルシステムを提供します。ZPL は OpenSolaris VFS インタフェースと背後の DMU インタフェースの間のギャップを埋めます。また、アクセス制御リスト (Access Control List、ACL) 規則や同期 (O_DSYNC) セマンティクスを適用します。
ZFS には、ストレージプール内の領域に基礎を置く raw デバイスを表示する機能が搭載されています。これらのデバイスはソースコード内では 'zvols' と呼ばれ、ZFS のソース内では単一ファイルによって実装されます。
このデバイスは libzfs のメイン制御点です。コンシューマは ioctl(2) インタフェースを直接利用できますが、これは libzfs と密接に絡み合っているため、公開インタフェースではありません。libzfs もそうです。これは単一ファイルで、ioctl() のパラメータの妥当性検査を行い、要求を ZFS 内の該当する場所に転送します。
DMU は SPA によって提供されるフラットアドレス空間に構築される
トランザクションオブジェクトモデルを提供します。コンシューマは DMU と対話するのにオブジェクトセット、オブジェクト、およびトランザクションを使用します。オブジェクトセットはオブジェクトのコレクションで、さらに、各オブジェクトは SPA からのストレージのさまざまな断片です。各トランザクションは一続きの操作で、グループとしてディスクにコミットする必要があります。トランザクションは ZFS のオンディスク整合性チェックの主要対象になるものです。
DSL は DMU オブジェクトセットを、継承したプロパティー、割り当て制限適用、および予約適用とともに階層的名前空間に統合します。また、オブジェクトセットのスナップショットおよびクローンの管理も行います。
スナップショットの実装方法については、「Matt のブログエントリ」を参照してください。
ZAP は DMU の上位に構築され、スケーラブルなハッシュアルゴリズムを使用してオブジェクトセット内にさまざまな名前とオブジェクトの関連付けを作成します。ZAP は ZPL 内にディレクトリを実装するのにもっともよく使用されますが、拡張して DSL 全体で使用されたり、プール全体にわたるプロパティーを格納する方法としても使用されます。ZAP には、非常に異なった 2 つのアルゴリズムがあり、それぞれ異なる種類のディレクトリ用にデザインされています。「マイクロ ZAP」は、エントリ数が比較的少なく各エントリがかなり短い場合に使用されます。「ファット ZAP」はそれより大きめのディレクトリや非常に長い名前のディレクトリに使用されます。
ZFS は常に一貫性のあるデータを提供しますが、データの大半がディスクに即座に書き込まれないという、従来からのセマンティクスを採用しています。このセマンティクスを採用しないと、パフォーマンスがひどく低下するためです。しかし、アプリケーションによっては、より厳格なセマンティクス、すなわち read(2) や write(2) 呼び出しが終了するまではデータがディスク上に残っていることが保証されるセマンティクスが要求されることがあります。このような動作 (O_DSYNC で指定されるもの) を要求するアプリケーションのために、ZIL では必要とされるセマンティクスを提供します。このセマンティクスでは、効率的なデータセット単位のトランザクションログを使用します。また、このログはクラッシュの場合に再実行できます。
ZIL 実装については、Neil の「
ブログエントリ」を参照してください。
トラバースは、ライブプール内のすべてのデータを安全かつ効率的に調べることができる方法で、再起動可能です。これは再同期化および消し込みの基盤になっています。トラバースは、すべてのメタデータを調べて、一定の期間内に変更されたブロックを検索します。その際、ZFS には書き込み時コピー特性があるため、機能停止時間に変更されなかった大規模なサブツリーが検索されないで済むという利点があります。トラバースは基本的に SPA の機能ですが、いくつかの DMU 構造の詳細な情報を使用してスナップショット、クローンなどのオンディスク書式の特性を処理します。
ZFS は Adaptive Replacement Cache の修正バージョンを使用して、1 次キャッシュの使用に必要になるものを提供します。このキャッシュは DMU と SPA の中間の層になり、仮想のブロックレベルで動作します。これにより、ファイルシステムがキャッシュしたデータをそのスナップショットやクローンと共有できます。
| arc.c | Adaptive Replacement Cache の実装です |
プールレイヤー全体が SPA (Storage Pool Allocator) としばしば呼ばれますが、このうちの構成部分こそが本当の公開インタフェースです。SPA が ZIO と vdev のレイヤーを張り合わせて、一貫性のあるプールオブジェクトにします。SPA には、プールを作成したり構成情報から破棄したりするルーチンや、
データを vdev に定期的間隔で同期化するルーチンが含まれています。
ZIO パイプラインは、ディスクに送受信されるすべてのデータが通過する場所です。これは DVA (Device Virtual Address) を vdev 上の論理的位置に変換するだけでなく、必要に応じてデータのチェックサムを計算したりデータを圧縮したりします。ZIO は複数ステージのパイプラインとして実装され、各 I/O に対してどのステージを実行するかを制御するビットマスクを持っています。パイプラインはきわめて複雑なため一言では説明できませんが、次の図で全体像がわかります。
| I/O タイプ |
ZIO 状態 |
圧縮 |
チェックサム |
ギャングブロック
| DVA 管理 |
Vdev I/O |
| RWFCI | オープン | | | | | |
| RWFCI | 次を待機: 子の準備完了 | | | | | |
| -W--- | | 圧縮書き込み | | | | |
| -W--- | | | チェックサムの生成 | | | |
| -WFC- | | | | ギャングのパイプライン処理 | | |
| -WFC- | | | | ギャングヘッダーの取得 | | |
| -W--- | | | | 書き換え: ギャング ヘッダー | | |
| --F-- | | | | 解放: ギャング のメンバー | | |
| ---C- | | | | 取り込み: ギャング のメンバー | | |
| -W--- | | | | | DVA の割り当て | |
| --F-- | | | | | DVA の解放 | |
| ---C- | | | | | DVA の取り込み | |
| -W--- | | | ギャングのチェックサム の生成 | | | |
| RWFCI | 準備完了 | | | | | |
| RW--I | | | | | | I/O 開始 |
| RW--I | | | | | | I/O 完了 |
| RW--I | | | | | | I/O 測定 |
| RWFCI | 次を待機: 子の完了 | | | | | |
| R---- | | | チェックサム確認 | | | |
| R---- | | | | 読み取り: ギャング のメンバー | | |
| R---- | | 非圧縮読み取り | | | | |
| RWFCI | 完了 | | | | | |
- I/O タイプ
- I/O パイプラインの各フェーズは特定タイプの I/O に適用されます。各文字は読み取り (Read、R)、書き込み (Write、W)、解放 (Free、F)、取り込み (Claim、C)、および入出力制御 (Ioctl、I) を意味します。
- ZIO 状態
- これらは I/O の同期をとるための内部状態です。たとえば、子 I/O がある I/O は、すべての子でブロックポインタの割り当て準備が完了するまで待つ必要があり、またすべての子が完了になるまで終了できません。
- 圧縮
- このフェーズは、適用可能な場合に任意の圧縮アルゴリズムを適用します。
- チェックサム
- このフェーズは、適用可能な場合に任意のチェックサムアルゴリズムを適用します。
- ギャングブロック
- ブロック全体を書き込めるだけの連続した十分な領域がない場合に、ZIO パイプラインは I/O を小さな「ギャングブロック」に分割し、あとで透過的にアセンブルして完全なブロックにします。
- DVA 管理
- 各 I/O には、プール内の vdev の特定部分に対応する DVA (Device Virtual Address) を割り当てる必要があります。このフェーズは、metaslab および領域マップを使用して、必要とされるこのアドレスの割り当てを行います。
- Vdev I/O
- このフェーズは、プール内に含まれる vdev への I/O を実際に実行するフェーズです。
これは仮想デバイスサブシステムで、デバイスを統一的に整列し、アクセスする手段になります。複数の仮想デバイスでツリーを構成し、ルート vdev が 1 つで、内部 vdev (ミラーおよび RAID-Z) およびリーフ vdev (ディスクおよびファイル) が複数存在します。各 vdev は利用可能な領域を表すとともに、物理ディスク上のブロックをレイアウトします。
前述のスタックの最下部で、ZFS は、LDI (Layered Driver Interface) および VFS インタフェース (ファイルの処理時) を使用して背後の物理デバイスと対話しています。