%20%E6%93%8D%E4%BD%9C%E3%81%A7%E7%85%A7%E4%BC%9A%E3%82%92%E6%AD%A3%E3%81%97%E3%81%8F%E5%AE%9F%E8%A3%85%E3%81%97%E3%81%BE%E3%81%99%E3%80%82.png)
readdir()
私はおもちゃのファイルシステムを実装しようとしており、効率的でスケーラブルな方法でそれを正しく実行する方法を見つけようとしています。 FUSEが使用するインタフェースを理解するために、主にマニュアルを読んだ。パイヒューズ3しかし、他のFUSEラッパーを使用しても問題は解決されるとは思いません。
私の理解は私が実装するときreaddir()
呼ばれると、呼ばれることを期待したreaddir_reply()
メソッドが返されるまで、連続したディレクトリエントリを持ちますFalse
。これを実行するときは、各エントリを名前と呼ばれる一意の[1] 64ビットIDに関連付けたいと思いますnext_id
。次の呼び出しはreaddir()
これらのIDの1つを受け取り、前の呼び出しからそのIDが返されると予想されます。 IDに関連付けられたエントリの後に始まるディレクトリエントリ。
呼び出し間でディレクトリが変更された場合(項目の追加または削除など)、連続呼び出しreaddir()
から追加された項目を含めるか削除した項目を省略するかを自由に選択できますが、他のすべての項目はIDを保持する必要があります。スキップまたは2回返されます。
意味では、これらすべてが私にとっては大丈夫だと思います。私が考えることができる最も簡単な実装は、すべてのディレクトリエントリを配列として読み込み、配列内opendir()
の各エントリのインデックスをIDとして使用することです。すべての項目を一度に読みたくない場合は、呼び出すたびreaddir()
に配列を構築できます。ただし、ファイルハンドルを解放する前に配列[2]を消去することはできません。
最新のファイルシステムは、数億のファイルを含むディレクトリを簡単に処理できます。私は、これらの実装がディレクトリファイルハンドルごとのディレクトリエントリ数の順序でメモリ割り当てを許可できないと仮定します(たとえば、1000万ファイル×エントリあたり100バイト= 1GB)。これらのファイルシステムは通常、FUSEを介さずにカーネルにほぼ排他的に実装されています。
これらすべての事実は、以下の記述の少なくとも1つが事実であるという結論を下します。
- FUSEの動作要件を誤って理解しました
readdir()
。 - 私が見ることができないこれらの要件を満たすより効率的なソリューションがあります。
- カーネル内のファイルシステムに実装できるより良いAPIがありますが、このAPIはこの状態を維持する必要はありません。
- ファイルシステムは、
readdir()
同等の機能を正しく実装しませんが、代わりにアプリケーションが通常気にしない方法で実装します。 - ファイルシステムはディレクトリをナビゲートするときにギガバイトのメモリを割り当てますが、誰もそれを気にしません。
だからそれはどれですか?
readdir()
他のファイルシステム実装が一般的に満たすすべての期待を満たす方法でFUSEタスクを効率的に実装する方法を理解したいと思います。
[1]: 単一ファイルハンドル内で一意です。
[2]:またはreaddir()
呼び出し時start_id
に設定することもできます0
。
答え1
同時修正とハードリンクの場合、効率性のためにクッキーBツリーが必要です。 POSIXでは、他の正しいoff_tメソッドがわかりません。
このコメントはほとんどの答えを提供すると思います。https://github.com/facebookexperimental/eden/blob/5cc682e8ff24ef182be2dbe07e484396539e80f4/eden/fs/inodes/TreeInode.cpp#L1798-L1833
参照リンクを含め、ここにコピーします。
ディレクトリを同時に変更する状況では、readdirを正しく実装するのは簡単ではありません。この関数は複数回呼び出されます。最初の読み取りで提供される off_t 値は 0 または最後の項目のオフセットに対応する値です。 (または eekdir と Telldir が指定された項目のオフセット値)。
POSIX 規則に準拠するには、ディレクトリ ストリーム全体にわたって一連の readdir 呼び出しが与えられると、変更されていないすべてのエントリが正確に一度返される必要があります。 readdir呼び出しの間に追加または削除されたエントリが返されることがありますが、必ずしもそうではありません。
したがって、 off_t は、順序付けされた項目のリストに対する索引で十分ではありません。エントリがリンクされていない場合、次のreaddirはそのエントリをスキップします。
1つのオプションは、off_tをアイテム名のハッシュで埋めることです。 off_tには63個の利用可能なビットがあります(初期要求用に予約されたゼロ値を除く)。実際には、SpookyHashV2 63ビットで十分ですが、競合のあるディレクトリを作成してアイテムが重複しているか、無限ループを引き起こす可能性があります。また、
off
次のreaddir以前に削除されたエントリを処理する方法もわかりません。 (ストリームから再起動する場所を見つける方法は?)
現在、Edenはハードリンクをサポートしていません。したがって、短期的には、inode番号をoff_tに保存し、それをinodeでソートされたアイテムリストのインデックスとして扱うことができます。これは、追加のインデックスがない場合は2次時間の複雑さを持ちますが、正確です。
長期的に、特にEdenのツリーディレクトリ構造がSQLiteまたは同様のものに格納されている場合は、eekdir / readdir Cookieインデックスを維持し、そのCookieを使用してエントリを列挙する必要があります。