1. はじめに Linux では、ext2、ext3、vfat など、さまざまなファイル システムを共存させることができます。同じファイル I/O システム コールのセットを使用することで、ファイルが配置されている特定のファイル システム形式に関係なく、Linux 内の任意のファイルを操作できます。さらに、ファイル システム間でファイル操作を実行できます。図 1 に示すように、cp コマンドを使用して、vfat ファイル システム形式のハード ディスクから ext3 ファイル システム形式のハード ディスクにデータをコピーできます。この操作には 2 つの異なるファイル システムが関係します。 図 1. ファイルシステム間のファイル操作 「すべてはファイルである」は Unix/Linux の基本的な哲学の 1 つです。 Unix/Linux では、通常のファイルだけでなく、ディレクトリ、キャラクタデバイス、ブロックデバイス、ソケットなどもファイルとして扱われます。これらはタイプが異なりますが、同じ操作インターフェースのセットが提供されます。 図2. すべてがファイルである 仮想ファイルシステムは、上記の 2 つの Linux 機能を実現するための鍵となります。仮想ファイルシステム (VFS) は、ユーザー空間プログラムにファイルシステム インターフェイスを提供する Linux カーネルのソフトウェア レイヤーです。また、異なるファイル システムを共存させる抽象関数をカーネル内に提供します。システム内のすべてのファイル システムは、共存するために VFS に依存するだけでなく、連携するためにも VFS に依存します。 さまざまな実際のファイル システムをサポートするために、VFS はすべてのファイル システムでサポートされる基本的な概念的なインターフェイスとデータ構造を定義します。同時に、実際のファイルシステムは、VFS が期待する抽象インターフェースとデータ構造も提供し、ファイルやディレクトリなどの独自の概念を形式的に VFS の定義と一貫性を保っています。つまり、実際のファイル システムを Linux でサポートするには、VFS で動作するために VFS 標準に準拠したインターフェイスを提供する必要があります。実際のファイル システムでは、特定の実装の詳細が統一されたインターフェイスとデータ構造の下に隠されているため、VFS レイヤーとカーネルの他の部分の観点からは、すべてのファイル システムは同じです。図 3 はカーネル内の VFS と実際のファイルシステム間の連携関係を示しています。 図3. VFSとカーネル内の他のカーネルモジュールとの協調関係 カーネルに VFS を導入したおかげで、ファイルシステム間のファイル操作が実現され、「すべてがファイルである」というスローガンが実現できることはすでにわかっています。なぜ VFS を導入することでこれら 2 つの機能を実現できるのでしょうか?次に、このアイデアから始めます。 この記事の主なトピック: まず、VFS モデルを記述するために使用されるいくつかのデータ構造を簡単に紹介し、これらのデータ構造間の関係をまとめます。次に、2 つの代表的なファイル I/O 操作 sys_open() と sys_read() を選択し、カーネルが VFS を使用して特定のファイル システムと対話し、ファイル システム間のファイル操作を実装する方法を詳しく説明し、「すべてがファイルである」というスローガンを約束します。 2 VFSデータ構造 2.1 基本的な概念 本質的に、ファイル システムは、ファイル、ディレクトリ、および関連する制御情報を含む特別な階層型データ ストレージ構造です。この構造を説明するために、Linux ではいくつかの基本的な概念が導入されています。 ファイルは、論理的に完全な一連の情報項目です。 Linux では、通常のファイルに加えて、ディレクトリ、デバイス、ソケットなどもファイルとして扱われます。つまり、「すべてがファイルである」ということです。 ディレクトリ ディレクトリは、関連するファイルを保持するフォルダーのようなものです。ディレクトリにはサブディレクトリを含めることができるため、ディレクトリをネストしてファイル パスを形成できます。 Linux では、ディレクトリも特殊ファイルとして扱われるため、ファイルに使用される操作はディレクトリにも使用できます。 ディレクトリ エントリ ファイル パスでは、パスの各部分はディレクトリ エントリと呼ばれます。たとえば、パス /home/source/helloworld.c では、ディレクトリ /、home、source、およびファイル helloworld.c はすべてディレクトリ エントリです。 インデックス ノードは、ファイルのメタデータを格納するために使用されるデータ構造です。ファイルのメタデータ、つまりファイルの関連情報は、ファイル自体とは異なります。ファイルのサイズ、所有者、作成時刻、ディスクの場所など、ファイルに関連する情報が含まれています。 スーパーブロックは、ファイル システムの制御情報を格納するために使用されるデータ構造です。ファイルシステムの状態、ファイルシステムの種類、サイズ、ブロック数、iノード数などを記述し、ディスクの特定のセクターに保存されます。 上記概念のディスク上の位置関係を図4に示す。 図4. ディスクとファイルシステム ファイル システムに関する 3 つの紛らわしい概念: 特定の方法でディスクを作成しフォーマットするプロセスは、ディスク上にファイル システムを構築するプロセスです。ファイル システムが作成されると、ファイル システムに関する制御情報がディスク上の特定の場所に書き込まれます。 カーネルに報告してカーネルからサポートを受けられることを宣言するために登録します。通常はカーネルをコンパイルするときに登録されます。モジュールをロードして手動で登録することもできます。登録プロセスでは、実際の各ファイル システムを表すデータ構造 struct file_system_type がインスタンス化されます。 インストールは、Linux ルート ファイル システムのディレクトリ ツリー構造にファイル システムを追加する、おなじみのマウント操作です。この方法でのみファイル システムにアクセスできます。 2.2 VFSデータ構造 VFS は、その構造情報を記述するために 4 つの主要なデータ構造といくつかの補助データ構造に依存します。これらのデータ構造はオブジェクトのように動作します。各メイン オブジェクトには、カーネルがこれらのメイン オブジェクトに対して実行できる操作を記述する操作関数テーブルで構成される操作オブジェクトが含まれています。 2.2.1 スーパーブロックオブジェクト マウントされたファイル システムを表す、マウントされたファイル システムの制御情報を格納します。実際のファイル システムがマウントされるたびに、カーネルはディスク上の特定の場所から制御情報を読み取り、メモリ内のスーパーブロック オブジェクトを埋めます。インストール インスタンスとスーパーブロック オブジェクトの間には 1 対 1 の対応があります。スーパーブロックは、その構造体のフィールド s_type を通じて、それが属するファイル システムのタイプを記録します。 3 番目の部分のソース コードを追跡する必要性に応じて、スーパー ブロック構造の関連するメンバー フィールドのいくつかの説明を次に示します (以下と同じ)。 リスト 1. スーパーブロック
2.2.2 インデックスノードオブジェクト インデックス ノード オブジェクトは、ファイルの関連情報を格納し、ストレージ デバイス上の実際の物理ファイルを表します。ファイルにアクセスすると、カーネルはメモリ内の対応するインデックス ノード オブジェクトを組み立てて、ファイルの操作に必要なすべての情報をカーネルに提供します。この情報の一部はディスク上の特定の場所に保存され、他の部分は読み込み時に動的に入力されます。 リスト 2. インデックスノード struct inode{//インデックス ノード構造体... struct inode_operations *i_op; /*インデックスノード操作テーブル*/ struct file_operations *i_fop; /*インデックスノードに対応するファイルのファイル操作セット*/ struct super_block *i_sb; /*関連スーパーブロック*/ ……};struct inode_operations { //インデックスノードメソッド... //この関数は、主にopen()システムコールによって呼び出され、dentryオブジェクトに対応するファイルの新しいインデックスノードを作成します。int (*create) (struct inode *、struct dentry *、int、struct nameidata *); //特定のディレクトリ内の dentry オブジェクトに対応するインデックス ノードを検索します struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *); ……}; 2.2.3 ディレクトリエントリオブジェクト ディレクトリ エントリの概念は、主にファイルの検索を容易にするために導入されました。パスの各コンポーネントは、ディレクトリであっても通常のファイルであっても、ディレクトリ エントリ オブジェクトです。たとえば、パス /home/source/test.c では、ディレクトリ /、home、source、およびファイル test.c はすべてディレクトリ エントリ オブジェクトに対応します。前の 2 つのオブジェクトとは異なり、ディレクトリ エントリ オブジェクトには対応するディスク データ構造がありません。 VFS は、パス名をトラバースするプロセス中に、それらをその場で 1 つずつディレクトリ エントリ オブジェクトに解析します。 リスト3. ディレクトリエントリ
2.2.4 ファイルオブジェクト ファイル オブジェクトは、メモリ内の開いているファイルの表現であり、主にプロセスとディスク上のファイル間の対応を確立するために使用されます。これは sys_open() によってオンサイトで作成され、 sys_close() によって破棄されます。ファイル オブジェクトと物理ファイルの関係は、プロセスとプログラムの関係に少し似ています。ユーザー空間から VFS を見ると、スーパーブロック、インデックスノード、またはディレクトリエントリを気にすることなく、ファイルオブジェクトのみを処理すればよいように見えます。複数のプロセスが同時に同じファイルを開いて操作できるため、同じファイルに対応するファイル オブジェクトが複数存在する場合があります。ファイル オブジェクトは、プロセスの観点から開かれたファイルを表すだけであり、プロセスの観点からは、ディレクトリ エントリ オブジェクト (inode を指す) を指します。ファイルに対応するファイル オブジェクトは一意ではない可能性がありますが、対応するインデックス ノードとディレクトリ エントリ オブジェクトは間違いなく一意です。 リスト4. ファイルオブジェクト
2.2.5 その他のVFSオブジェクト 2.2.5.1 ファイルシステム関連 さまざまなファイル システム タイプは、ファイル システムが配置されている物理メディアと、物理メディア上でのデータの編成方法に基づいて区別されます。 file_system_type 構造体は、特定のファイル システムのタイプ情報を記述するために使用されます。 Linux でサポートされている各ファイル システムには、システムにインストールされているインスタンスが 0 個以上あるかどうかに関係なく、file_system_type 構造体が 1 つだけあります。 同様に、ファイル システムが実際にインストールされるたびに、マウント ポイントに対応する vfsmount 構造が作成されます。 リスト5. ファイルシステム関連
2.2.5.2 プロセス関連 リスト6. ファイルセットを開く
2.2.5.3 経路探索に関連する リスト7. 補助検索
2.2.6 オブジェクト間の関係 上記のデータ構造は単独で存在するわけではありません。 VFS が適切に機能できるのは、それらの有機的なつながりを通じてです。次の図はそれらの間の接続を説明しています。 図 5 に示すように、Linux でサポートされている各ファイル システムには、システムにインストールされているインスタンスが 0 個かそれ以上かに関係なく、file_system_type 構造体が 1 つだけあります。ファイル システムがインストールされるたびに、対応するスーパー ブロックとインストール ポイントが存在します。スーパーブロックは、フィールド s_type を通じて対応する特定のファイルシステム タイプを指します。特定のファイル システムは、file_system_type のフィールド fs_supers を通じて同じファイル タイプのスーパー ブロックをリンクします。同じファイル システム タイプのスーパーブロックは、s_instances フィールドを介してリンクされます。 図5. スーパーブロック、マウントポイント、特定のファイルシステムの関係 図 6 に示すように、プロセスは task_struct のフィールド files_struct files を使用して、現在開いているファイル オブジェクトを認識します。通常、ファイル記述子と呼ばれるものは、実際にはプロセスによって開かれたファイル オブジェクトの配列のインデックス値です。ファイル オブジェクトは、ドメイン f_dentry を通じて対応する dentry オブジェクトを見つけ、次に dentry オブジェクトのドメイン d_inode を通じて対応するインデックス ノードを見つけ、これにより、ファイル オブジェクトと実際の物理ファイルとの関連付けが確立されます。 ***、もう 1 つの重要な点は、ファイル オブジェクトに対応するファイル操作関数のリストが、インデックス ノードのフィールド i_fop を通じて取得されることです。図 6 は、第 3 部のソース コードを理解する上で大きな役割を果たします。 図6. プロセス、スーパーブロック、ファイル、インデックスノード、ディレクトリエントリの関係 3 VFSベースのファイルI/O これまでのところ、この記事では主に理論的な観点から VFS の動作メカニズムについて説明してきました。次に、ソース コード レイヤーを深く掘り下げて、2 つの代表的なシステム コール sys_open() と sys_read() を説明し、VFS が特定のファイル システムに提供するインターフェイス メカニズムをより深く理解します。この記事では、ファイル操作の全体的なプロセス システムに重点を置いているため、ソース コードをトレースするときには、いくつかの詳細な処理には注意を払いません。スペースの制限により、関連するコードのみがリストされます。この記事のソース コードは、linux-2.6.17 カーネル バージョンからのものです。 sys_open() と sys_read() について詳しく説明する前に、まず sys_read() が呼び出されるコンテキストを見てみましょう。図 7 は、ユーザー空間での read() 呼び出しからディスクからデータが読み取られるまでのプロセス全体を示しています。ユーザー アプリケーションがファイル I/O read() 操作を呼び出すと、システム コール sys_read() がトリガーされます。 sys_read() は、ファイルが配置されている特定のファイル システムを見つけ、そのファイル システムに制御を渡します。最後に、特定のファイル システムが物理メディアと対話し、メディアからデータを読み取ります。 図7. 物理メディアからデータを読み取るプロセス 3.1 sys_open() sys_open() システム コールはファイルを開くか作成し、成功した場合はファイルのファイル記述子を返します。図 8 は、sys_open() 実装コード内の主な関数呼び出しの図です。 図8. sys_open関数呼び出し関係図 sys_open() はコード量が多く、関数呼び出し関係も複雑なので、以下では主に関数全体の分析を行います。いくつかの重要なポイントについては、キーコードがリストされています。 a. sys_open() の関数呼び出し図から、いくつかの簡単なパラメータ チェックを実行した後、sys_open() が do_sys_open() にバトンを渡すことがわかります。 1) まず、get_unused_fd() は使用可能なファイル記述子を取得します。この関数により、ファイル記述子が実際にはプロセスのオープン ファイル リスト内のファイル オブジェクトのインデックス値であることがわかります。 2) 次に、do_filp_open() はファイルを開き、プロセスによって開かれたファイルを表すファイル オブジェクトを返します。プロセスは、このようなデータ構造を通じて物理ファイルを読み書きします。 3) ***、fd_install() はファイル記述子とファイル オブジェクト間の接続を確立します。その後、プロセスはファイル記述子を操作してファイルを読み書きします。 b. do_filp_open() はファイルを開いてファイル オブジェクトを返すために使用されます。開く前に、まずファイルを見つける必要があります: 1) open_namei() は、パス情報を保持するデータ構造 nameidata を使用して、ファイル パス名に基づいてファイルを検索するために使用されます。 2) 検索が完了すると、パス情報が入った nameidata が次の関数 nameidata_to_filp() に返され、最終的なファイル オブジェクトが取得されます。目的が達成されると、nameidata データ構造は直ちに解放されます。 c.open_namei() はファイルを見つけるために使用されます: 1) path_lookup_open() はファイル検索機能を実装します。開くファイルが存在しない場合は、新しいプロセスが必要になり、path_lookup_create() が呼び出されます。後者と前者は同じ実際のパス検索機能をカプセル化しますが、パラメータが異なるため、処理の詳細が異なります。 2) ファイルを新しいファイルとして開く場合、つまり O_CREAT フラグが設定されている場合は、新しいインデックス ノードを作成する必要があり、これはファイルの作成を意味します。 vfs_create() のコアステートメント dir->i_op->create(dir, dentry, mode, nd) から、特定のファイルシステムによって提供されるインデックスノードを作成するためのメソッドを呼び出していることがわかります。注: ここでのインデックス ノードの概念はメモリ内にのみ配置されます。ディスク上の物理インデックス ノードとの関係は、メモリ内およびディスク上にあるファイルのようなものです。現時点では、新しく作成されたインデックス ノードは、物理ファイルの作成が成功したことを完全にマークできません。インデックス ノードがディスクに書き戻された場合にのみ、物理ファイルが実際に作成されます。新しい方法でファイルを開き、読み書きし、最後に保存せずに閉じると想像してください。メモリ内のインデックス ノードは作成から消滅までのプロセスを経ますが、ディスクは誰かがファイルを作成しようと考えたことを知ることはありません。これは、インデックス ノードが書き戻されないためです。 3) path_to_nameidata() は nameidata データ構造を埋めます。 4) may_open() はファイルを開くことができるかどうかを確認します。リンクファイルや書き込み権限のみを持つディレクトリなど、一部のファイルは開くことができません。まず、nd->dentry->inode によって指されるファイルがこのタイプのファイルであるかどうかを確認します。その場合はエラーが返されます。 TRUNC モードで開くことができないファイルもいくつかあります。 nd->dentry->inode が指すファイルがこのカテゴリに属する場合は、TRUNC フラグを明示的にオフにします。次に、ファイルがTRUNCモードで開かれている場合は、nd->dentry->inodeの情報を更新します。 3.1.1__path_lookup_intent_open() path_lookup_open() であっても path_lookup_create() であっても、最終的には __path_lookup_intent_open() を呼び出してファイルを検索する機能を実装します。検索時にパスをトラバースするプロセスで、各パス コンポーネントがレイヤーごとにディレクトリ エントリ オブジェクトに解析されます。このディレクトリ エントリ オブジェクトがディレクトリ エントリ キャッシュ内にある場合は、キャッシュから直接取得されます。ディレクトリ エントリがキャッシュ内に存在しない場合は、実際のディスク読み取り操作が実行され、ディレクトリ エントリに対応するインデックス ノードがディスクから読み取られます。インデックス ノードを取得した後、インデックス ノードとディレクトリ エントリ間の接続が確立されます。このサイクルは、ターゲット ファイルに対応するディレクトリ エントリが最終的に見つかり、インデックス ノードが見つかるまで続きます。インデックス ノードから対応するスーパー ブロック オブジェクトを見つけることで、ファイルが配置されているファイル システムの種類を判別できます。ディレクトリエントリに対応するインデックスノードをディスクから読み取ります。これにより、VFS と実際のファイル システム間の相互作用がトリガーされます。前回の VFS 理論の紹介から、インデックス ノードを読み取る方法はスーパー ブロックによって提供されることがわかります。実際のファイルシステムがインストールされると、メモリ内に作成されたスーパーブロック情報に実際のファイルシステムの関連情報が書き込まれます。ここでの関連情報には、実際のファイルシステムで定義されているスーパーブロックの操作関数のリスト、そしてもちろん、インデックスノードの読み取りの具体的な実行方法が含まれます。実際のファイルシステム ext3 の ext3_read_inode() を追跡し続けると、この関数の重要なタスクは、異なるファイルタイプに対して異なるインデックスノード操作関数テーブルとファイル操作関数テーブルを設定することであることがわかります。 リスト8. ext3_read_inode
3.1.2 nameidata_to_filp サブ関数: __dentry_open これは、VFS が実際のファイル システムに接続する重要なポイントです。セクション 3.1.1 の分析から、実際のファイル システムのメソッドを呼び出してインデックス ノードを読み取ると、実際のファイル システムは、ファイルの種類に応じて、インデックス ノードに異なるファイル操作関数のセットを割り当てることがわかります。例えば、通常のファイルには通常のファイルに対応する操作関数のセットがあり、デバイス ファイルにはデバイス ファイルに対応する操作関数のセットがあります。このように、対応するインデックス ノードのファイル操作関数セットがファイル オブジェクトに割り当てられると、将来的に読み取り操作などのファイル操作が行われるときに、VFS はさまざまな異なるファイルに対して同じ read() 操作インターフェイスを実行しますが、実際に読み取るときには、カーネルは異なるファイル タイプを区別して処理する方法を認識します。 リスト9. __dentry_open
3.2 sys_read() sys_read() システムコールは、開かれたファイルからデータを読み取るために使用されます。読み取りが成功した場合、読み取られたバイト数が返されます。ファイルの末尾に達した場合は 0 が返されます。図 9 は、sys_read() 実装コード内の関数呼び出し関係図です。 図9. sys_read関数呼び出し図 ファイルを読み取るときは、まずファイルを開く必要があります。 3.1 の要約から、ファイルを開くと、ファイル オブジェクトがメモリ内に組み立てられ、ファイルに対して実行する操作メソッドがファイル オブジェクトに設定されていることがわかります。したがって、ファイルを読み取るとき、VFS はいくつかの簡単な変換 (ファイル記述子から対応するファイル オブジェクトを取得する。基本的な考え方は、current->files->fd[fd] が指すファイル オブジェクトを返すことです) を行った後、file->f_op->read(file, buf, count, pos) ステートメントを通じて実際のファイル システムの対応するメソッドを簡単に呼び出してファイルを読み取ることができます。 4. 問題解決 4.1 ファイルシステム間のファイル操作の基本原則 この時点で、Linux でファイルを複数のファイルシステム間で操作できる理由を説明できます。たとえば、vfat ディスク上のファイル a.txt を ext3 ディスクにコピーし、b.txt という名前を付けます。これには、a.txt の読み取りと b.txt の書き込みという 2 つのプロセスが含まれます。読み書きする前に、ファイルを開く必要があります。前の分析から、ファイルを開くときに、VFS はファイルに対応するファイル システム形式を認識することがわかります。今後ファイルを操作する際には、VFS は対応する実際のファイルシステムの操作メソッドを呼び出します。したがって、VFS は vfat のファイル読み取りメソッドを呼び出して、a.txt のデータをメモリに読み込みます。メモリ内の a.txt のデータを b.txt に対応するメモリ空間にマッピングした後、VFS は ext3 のファイル書き込みメソッドを呼び出して b.txt をディスクに書き込みます。これにより、最終的なファイルシステム間のコピー操作が実現されます。 4.2 「すべてがファイル」の実装の基礎 通常のファイル、特殊なディレクトリ、デバイスなど、VFS はそれらをファイルとして扱い、同じファイル操作インターフェイスのセットを介して操作します。ファイルを操作するときは、まずファイルを開く必要があります。ファイルを開くと、VFS はファイルに対応するファイル システム形式を認識します。 VFS が実際のファイル システムに制御を渡すと、実際のファイル システムは特定の区別を行い、異なるファイル タイプに対して異なる操作を実行します。これが「すべてがファイルである」の本質です。 5 結論 VFS または仮想ファイルシステムは、Linux ファイルシステム内の抽象ソフトウェアレイヤーです。このサポートにより、Linux ではさまざまな実際のファイルシステムが共存でき、ファイルシステム間の操作を実現できます。 VFS は、スーパー ブロック、インデックス ノード、ディレクトリ エントリ、ファイル オブジェクトという 4 つの主要なデータ構造といくつかの補助データ構造を使用して、通常のファイルやディレクトリ、デバイス、ソケットなど、Linux に同じ操作インターフェイス (開く、読み取る、書き込む、閉じるなど) を提供します。実際のファイル システムに制御が渡された場合にのみ、実際のファイル システムは区別を行い、異なるファイル タイプに対して異なる操作を実行します。 VFS の存在があるからこそ、ファイルシステム間の操作が可能になり、Unix/Linux における「すべてがファイル」というスローガンが実現できることがわかります。 |
>>: Appleがクラウドネイティブコンピューティング財団に参加
包括的な「ディープクラウド導入」時代の到来により、ますます多くの中国企業がクラウドネイティブ技術を導...
世界的に有名なコンピュータールーム zenlayer 傘下のクラウドサーバーブランド arkecx ...
最近、多くのウェブマスターの友人とチャットをしていますが、オンラインプロモーションについて話すと、草...
ブルガリアのDA International Group Ltd傘下のalphavpsブランド(AS...
先月9日、HostCatはserverhubの無料仮想ホスト「serverhub-free仮想ホスト...
hivelocity.net 傘下の VPS クラウド ブランド sparknode が値下げされた...
一昨日、「hostdare - 1.79 USD/512M メモリ/30G SSD/1T トラフィッ...
インターネット大手に最近何をしているのかと尋ねた場合、「野菜を売っている」という明白な答えは、実は間...
外部リンクは、Web サイトを最適化したり宣伝したりするための最も重要な方法の 1 つです。これは多...
現在、ほとんどのウェブサイトの主な収入源は広告です。しかし、多くのウェブマスターは、広告収入が低いこ...
ミニプログラムは発売当初、 UIデザイン、サイズ制限、ユーザーエクスペリエンスなどの問題で業界から批...
Baidu アルゴリズムの継続的な調整により、多くの個人ウェブマスターは、正しいウェブサイト最適化方...
今日、AI クラウド サービスは、データ サイエンティストや開発者を惹きつけ、自社のプラットフォーム...
2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っています昨今、オン...
webfaction は創立 10 周年を記念して、現在、仮想ホスティング料金 100 ドルを全員に...