GNU/Linux >> Linux の 問題 >  >> Linux

Linux で、ディレクトリ内の 1000 個のファイルが別の場所に移動され、別の 300 個のファイルがソース ディレクトリに追加された場合はどうなりますか?

これは、使用するツールによって異なります。いくつかのケースを確認してみましょう:

mv /path/to/source/* /path/to/dest/ の行に沿って何かを実行すると シェルを使用すると、元の 1000 個のファイルが移動され、新しい 300 個のファイルは変更されないことになります。これは、シェルが * を展開するという事実に由来します。 移動操作を開始する前に、移動が進行中の場合、リストは既に固定されています。

Nautilus (およびその他の GUI フレンド) を使用する場合、最終的には同じ方法になります:選択されたファイルに基づいて移動操作が実行されます。これは、新しいファイルが表示されても変わりません。

glob のループの行に沿ってシステムコールを使用して独自のプログラムを使用する場合 そして1つだけ mv globまで 空のままにすると、新しいディレクトリに 1300 個のファイルすべてが作成されます。これは、新しい glob ごとに その間に表示された新しいファイルを取得します。


ディレクトリからすべてのファイルを移動するようにシステムに指示すると、システムはすべてのファイルを一覧表示してから、それらの移動を開始します。新しいファイルがディレクトリに表示された場合、それらは移動するファイルのリストに追加されないため、元の場所に残ります。

もちろん、mv とは異なるファイルの移動方法をプログラムすることもできます。 これにより、ソース ディレクトリ内の新しいファイルが定期的にチェックされます。


カーネル自体が「1000 ファイルの移動」操作の「途中」になることはできません。提案している操作について、より具体的にする必要があります。

rename(*oldpath, const char *newpath) を使用すると、1 つのスレッドで一度に 1 つのファイルしか移動できません または renameat システムコール (同じファイルシステム内のみ)。または Linux renameat2 RENAME_EXCHANGE のようなフラグがあります 2 つのパス名、または RENAME_NOREPLACE をアトミックに交換するには しない 宛先が存在する場合は置き換えます。 (例:mv -i を許可する stat の競合状態を回避する実装 そして rename stat 以降に作成されたファイルを上書きします。 .link + unlink link のため、それも解決できます 新しい名前が存在する場合は失敗します。)

しかし、これらのシステム コールはそれぞれ、システム コールごとに 1 つのディレクトリ エントリの名前のみを変更します . POSIX renameat の使用 olddirfd で と newdirfd (open(O_DIRECTORY) で開く ) ソースまたは宛先ディレクトリ 自体 であっても、ディレクトリ内のファイルをループし続けることができます 改名されていました。 (相対パスを使用すると、通常の rename() でも可能になります .)

とにかく、他の回答が言うように、rename システム コールを使用するほとんどのプログラムは、最初の rename を実行する前にファイル名のリストを把握します。 . (通常は readdir(3) Linux getdents などのプラットフォーム固有のシステム コールのラッパーとしての POSIX ライブラリ関数 ).

しかし、あなたが find -exec ... {} \; について話しているのなら ファイルごとに 1 つのコマンドを実行するか、より効率的な -exec {} + 1 つのコマンド ラインに収まらないほど多くのファイルがある場合は、スキャン中に名前の変更が発生する可能性があります。例

find . -name '*.txt' -exec mv -t ../txtfiles {} \;   # Intentionally inefficient

新しい .txt を作成した場合 可能性 ../txtfiles でそれらのいくつかを参照してください .しかし、内部的には find(1) open(O_DIRECTORY) を使用します と getdents . で .

すべてを返すのに 1 つのシステム コールで十分だった場合 . のディレクトリ エントリ (これは一度に 1 つずつループし、-type に必要な場合にのみさらにシステム コールを行います。 または再帰するか、または fork+exec に一致する場合)、リストはある時点でのディレクトリエントリのスナップショットです。ディレクトリをさらに変更しても、find には影響しません これは、ループする内容をリストしたディレクトリのコピーが既にあるためです。 (おそらく内部的に readdir(3) を使用しています) 、一度に 1 つのエントリを返しますが、glibc 内部では strace find . を使用することでわかっています getdents64 になること count=32768 のバッファ サイズのシステム コール エントリ)

しかし、ディレクトリが巨大である場合、および/またはカーネルが find を満たしていない場合 のバッファでは、最初に取得したものをループした後、2 回目の getdents システム コールを行う必要があります。そのため、名前を変更した後に新しいエントリが表示される可能性があります。

ただし、他の回答の下にあるコメントの議論を参照してください。(私が思うに) getdents は同じファイル名を 2 回返すことが許可されていないため、カーネルがスナップショットを作成した可能性があります。ファイルシステムが異なれば、巨大なディレクトリ内のエントリへのアクセスを線形検索よりも効率的にするために、異なるソート/インデックスメカニズムが使用されます。そのため、ディレクトリを追加または削除すると、残りのエントリの順序に他の影響が及ぶ可能性があります。うーん、おそらく、ファイルシステムが安定した順序を維持し、実際のインデックスを更新する可能性が高くなります (EXT4 dir_index のように) 機能)、したがって、ディレクトリ FD の位置は、再開するディレクトリ エントリにすぎませんか? telldir(3) ライブラリ インターフェイスは lseek にマップされます 、またはそれが純粋にユーザー空間によって取得されたバッファーをループするためのユーザー空間のものである場合。しかし複数の getdents 巨大なディレクトリからすべてのエントリを取得するために必要になる場合があるため、シークがサポートされていなくても、カーネルは現在の位置を記録できる必要があります。

脚注 1:

ファイルシステム間で「移動」するには、ユーザー空間をコピーしてリンクを解除する必要があります。 (例:open および read+write のいずれか 、 mmap+write または sendfile(2) または copy_file_range(2) 、後者の 2 つは、ユーザー空間を介したファイル データのバウンスを完全に回避します。)


Linux
  1. Linux のスパース ファイルとは

  2. 指定したファイルが移動または削除された場合、Linux で開いているファイル ハンドルはどうなりますか

  3. Linux のメッセージ キューの短所は何ですか?

  1. 現在の Linux カーネル ソースは何ですか?

  2. Linux での lsof と netstat の違いは何ですか?

  3. Linux と Windows の .txt ファイル (Unicode エンコーディング) の違いは何ですか?

  1. Linux の一時ディレクトリはどこにありますか?

  2. Linuxで100万枚の画像をあるディレクトリから別のディレクトリに移動する最速の方法は何ですか?

  3. Linuxでの理想的なホームディレクトリのアクセス許可はどうあるべきか