出力ファイルが/ dev / nullのときにtarがファイルの内容をスキップするのはなぜですか?

出力ファイルが/ dev / nullのときにtarがファイルの内容をスキップするのはなぜですか?

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/nullGNU 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そうすべきですか?避ける他のすべてのケースでは。

関連情報