web-dev-qa-db-ja.com

ハードウェアリソースに波及しないオンザフライストリーム圧縮?

200 GBの空きディスク領域、16 GBのRAM(そのうち1 GBはデスクトップとカーネルによって占有されています)、および6 GBのスワップがあります。

240 GBの外部SSDがあり、70 GBを使用しています1 残りは無料で、ディスクにバックアップする必要があります。

通常、私は最初にディスクをdd if=/dev/sdb of=Desktop/disk.imgし、次にそれを圧縮しますが、最初にイメージを作成することはオプションではありません。圧縮ステップを実行すると、最終的なアーカイブが私のディスクに簡単に収まるように、スペースが押しつぶされています。

ddはデフォルトでSTDOUTに書き込み、gzipはSTDINから読み取ることができるため、理論的にはdd if=/dev/sdb | gzip -9 -を書き込むことができますが、gzipはバイトの読み取りにかなり時間がかかりますddはそれらを生成できます。

man pipe から:

パイプの書き込み側に書き込まれたデータは、パイプの読み取り側から読み取られるまでカーネルによってバッファリングされます。

私は|を実際のパイプのように視覚化します。1つのアプリケーションがデータを入力し、もう1つのアプリケーションがパイプのキューからデータをできるだけ早く取り出します。

左側のプログラムがパイプの反対側で処理できるよりも多くのデータをより速く書き込むとどうなるでしょうか。極端なメモリまたはスワップの使用を引き起こすか、またはカーネルがFIFO=を作成してディスクをいっぱいにしようとしますか?または、バッファがSIGPIPE Broken pipeで失敗するだけです。大きすぎる?

基本的に、これは2つの質問に要約されます:

  1. 一度に読み取られるよりも多くのデータをパイプに押し込むことの意味と結果は何ですか?
  2. 圧縮されていないデータストリーム全体をディスクに置かずに、データストリームをディスクに圧縮する信頼できる方法は何ですか?

注1:最初の70使用済みGBを正確にコピーすることはできません。断片化やその他の完全なコンテンツを完全な状態にする必要があるため、システムまたはファイルシステムが動作することを期待しています。

23
cat

技術的には、ddも必要ありません。

_gzip < /dev/drive > drive.img.gz
_

ddを使用する場合は、常に_dd bs=1M_のようなデフォルトのブロックサイズよりも大きくするか、syscall hell(ddのデフォルトのブロックサイズは512バイトであるため、read()およびwrite()は、MiBごとの_4096_システムコールです。オーバーヘッドが多すぎます)。

_gzip -9_は、表示するCPUが非常に少ない状態で、より多くのCPUを使用します。 gzipによって速度が低下する場合は、圧縮レベルを下げるか、別の(高速な)圧縮方法を使用してください。

ddイメージの代わりにファイルベースのバックアップを実行している場合は、圧縮するかどうかを決定するロジックを使用できます(さまざまなファイルタイプに対して圧縮する意味はありません)。 dartar Alternative`)は、そのためのオプションがある1つの例です。

空き容量がゼロの場合(TRIMの後に確実にゼロを返すSSDであり、fstrimを実行してキャッシュを削除したため)、ddを_conv=sparse_フラグとともに使用して、ゼロ領域にゼロディスクスペースを使用する、非圧縮のループマウント可能なスパースイメージ。スパースファイルをサポートするファイルシステムで画像ファイルをバックアップする必要があります。

あるいは、一部のファイルシステムでは、使用された領域のみを画像化できるプログラムが存在します。

16
frostschutz

ddは、一度に1ブロックずつデータを読み書きします。未処理のブロックは1つだけです。そう

_valgrind dd if=/dev/zero status=progress of=/dev/null bs=1M
_

は、ddが約1MBのメモリを使用することを示しています。ブロックサイズをいじってvalgrindをドロップすると、ddの速度への影響を確認できます。

gzipにパイプすると、ddgzipの速度に合わせて単に減速します。そのメモリ使用量は増加せず、カーネルがバッファをディスクに保存することもありません(カーネルはvia swapを除いて、その方法を知りません)。パイプの破損は、パイプの一方の端が死んだときにのみ発生します。詳細については、signal(7)およびwrite(2)を参照してください。

したがって

_dd if=... iconv=fullblock bs=1M | gzip -9 > ...
_

あなたがしていることをするための安全な方法です。

パイプするとき、読み取りプロセスが追いついていない場合、書き込みプロセスはカーネルによってブロックされます。あなたは実行することでこれを見ることができます

_strace dd if=/dev/zero bs=1M | (sleep 60; cat > /dev/null)
_

ddが1 MBを読み取り、次にwrite()を発行して、sleepの実行中に1分間待機します。これがパイプの両側のバランスです。カーネルは、書き込みプロセスが速すぎる場合は書き込みをブロックし、読み込みプロセスが速すぎる場合は読み取りをブロックします。

19
Stephen Kitt

パフォーマンス以外に悪影響はありません。パイプには通常64Kのバッファーがあり、その後、パイプへの書き込みはgzipがさらにデータを読み取るまで単純にブロックされます。

9
Ulrich Schwarz

それがどのように機能するかについての実際の質問に答える:「左側のプログラムがパイプの反対側がそれを処理することを期待できるよりも速くより多くのデータを書き込む場合はどうなりますか?」

これは起こりません。パイプには、かなり小さく制限されたサイズのバッファがあります。参照 パイプバッファの大きさ

パイプバッファがいっぱいになると、送信プログラムはblocksします。書き込み呼び出しを行うと、データがバッファーに書き込まれるまで、カーネルは制御をプログラムに返しません。これは、バッファを空にするための読み取りプログラムのCPU時間を与えます。

8
pjc50

たぶん、必要なのはファイルだけで、次にtarを使用します。あなたが欲しいものを何も含まないブロックをゼロで埋めることができます、誰かがすでにそれについて尋ねました。 ゼロで未使用のスペースをクリア(ext3、ext4)

次に、pigzがあり、これは通常gzipより高速です。

3
yt7b97q-