400GiBを超えるデータを含むディレクトリがあります。すべてのファイルをエラーなく読み取ることができるかどうかを確認したいので、私が考えた簡単な方法はtar
これです/dev/null
。
$ time tar cf /dev/null .
real 0m4.387s
user 0m3.462s
sys 0m0.185s
$ time tar cf - . > /dev/null
real 0m3.130s
user 0m3.091s
sys 0m0.035s
$ time tar cf - . | cat > /dev/null
^C
real 10m32.985s
user 0m1.942s
sys 0m33.764s
上記の3番目のコマンドは、Ctrl長時間実行した後に強制的に停止しました。Cさらに、最初の2つのコマンドが機能しているときに含まれるストレージデバイスのアクティビティインジケータは、ほとんど.
常にアイドル状態です。 3番目のコマンドが実行されると、インジケータが点灯し続けていて、非常に忙しいという意味です。
この観点から見ると、tar
出力ファイルが見つかると、/dev/null
つまり/dev/null
書き込み用のファイルハンドルを直接開くと、tar
ファイル本文がスキップされます。 (v
オプションを追加すると、tar
ディレクトリ内のすべてのファイルがtar
「赤」で印刷されます。)
だから知りたいです。なぜこのようなことが起こるのでしょうか?一種の最適化ですか?それでは、なぜtar
この特定の事例に対してそんなに疑わしい最適化がなされたのでしょうか。
私はLinux 4.14.105 amd64でGNU tar 1.26とglibc 2.27を使用しています。
答え1
それはい 記録された最適化:
アーカイブが作成されると、
/dev/null
GNU tar は入出力操作を最小化しようとします。 GNU tarでAmandaバックアップシステムを使用するときにこの機能を使用する初期サイズ変更プロセスがあります。
答え2
これはさまざまなプログラムで発生する可能性があります。たとえば、cp file /dev/null
使用時にこの動作を経験したことがあります。このコマンドはディスクの読み取り速度を予測しませんが、数ミリ秒後に返されます。
私が覚えている限り、それはSolarisまたはAIXにありましたが、この原則はあらゆる種類のunix-yシステムに適用されます。
過去には、プログラムがファイルをどこかにコピーするときにread
ディスク(またはファイル記述子が参照するすべて)の一部のデータをメモリにインポートするための呼び出し(返されたときにすべてがそこにあることを保証read
)とインポート呼び出しを交互に実行しますwrite
。しました。メモリブロックを削除し、コンテンツを宛先に送信します。
しかし、同じ目標を達成するには、少なくとも2つの新しい方法があります。
Linuxにはシステムコール
copy_file_range
(他のUNIXにはまったく移植可能ではありません)とsendfile
(やや移植可能で、元のネットワークにファイルを転送するように設計されていますが、今ではすべてのターゲットで利用可能です)があります。その目的は、伝送を最適化することです。プログラムがこれらのいずれかを使用している場合、カーネルがターゲットを認識し、/dev/null
システムコールをランダムに変更することを想像するのは簡単です。mmap
プログラムは代わりにファイルの内容を取得するために使用できますread
。これは、デフォルトでは、「システムコールが返されたときにデータがあることを確認する」の代わりに、「対応するメモリブロックにアクセスしようとしたときにデータがあることを確認する」ことを意味します。したがって、プログラムはmmap
ソースファイルをインポートし、マップされたwrite
メモリブロックを呼び出すことができます。ただし、書き込みには/dev/null
作成されたデータへのアクセスは必要ないため、「ファイルがあることを確認してください」という条件がトリガーされず、ファイルを読み取ることもできません。
gnu tarが書き込み操作を検出するときにこれらのメカニズムの1つを使用しているのか、どのメカニズムを使用しているのかはわかりませんが、/dev/null
これがすべてのプログラムの理由です。読み取り速度を確認するために使用する場合| cat > /dev/null
、-runの代わりに使用する必要があります> /dev/null
。なぜ| cat > /dev/null
そうすべきですか?避ける他のすべてのケースでは。