ddはデータのコピーに適していますか?(または、read()およびwrite()が部分的である場合)


60

短いバージョン:どのような状況でddデータのコピーに使用しても安全なのは、部分的な読み取りまたは書き込みによる破損のリスクがないことを意味しますか?

ロングバージョン—プリアンブル: dd多くの場合、特にデバイスとの間でデータをコピーするために使用されます()。これは、(実際にはマジックをやっているデバイスファイルの場合)、時には他のツールよりも低いレベルでのデバイスにアクセスすることができるという神秘的な性質を起因だ-まだdd if=/dev/sda同じものですcat /dev/sdaddより速いと考えられることもありますがcat、実際にはそれを打ち負かすことできます。それにもかかわらず、dd時にはそれを本当に便利にするユニークな特性があります。

問題: dd if=foo of=bar実際には、と同じではありませんcat <foo >bar。ほとんどのuni¹¹で、ddを1回呼び出しますread()。(。の「入力ブロックの読み取り」を構成するものでPOSIXがあいまいであることがわかりますdd。)read()部分的な結果を返す場合(POSIXおよび他の参照文書によると、実装ドキュメントに特に記載がない限り許可されます)、部分的なブロックがコピーされます。まったく同じ問題が存在しwrite()ます。

観察:実際には、ddブロックデバイスと通常のファイルに対処できることがわかりましたが、それはあまり練習していない可能性があります。パイプのことになると、過ちを犯すことは難しくありませんdd。たとえば、次のコードを試してください:

yes | dd of=out bs=1024k count=10

outファイルのサイズを確認します(10MBを大きく下回る可能性があります)。

質問:どのような状況でddデータのコピーに使用しても安全ですか?言い換えると、ブロックサイズ、実装、ファイルタイプなどのどの条件が、ddすべてのデータをコピーすることを保証できますか?

GNU ddには、ブロック全体を転送するためfullblockに呼び出しread()またはwrite()ループするように指示するフラグdd iflag=fullblockがあります。常に安全です。私のフラグは、これらのフラグ(他の実装には存在しない)が使用されない場合に関するものです)

¹ 私は、OpenBSD、GNUのcoreutilsのとBusyBoxの上でチェックしました。


私は実際に単一の読み取り(2)で、いくつかのMIBを読み取る可能性のあるUnixyシステムを見たことがない...
vonbrand

3
を使用するcount場合、iflag=fullblockは必須です(または、代わりにiflag=count_bytes)。ありませんoflag=fullblock
frostschutz

回答:


10

仕様から:

  • 場合bs=exprオペランドを指定しないと以外に変換されるsyncnoerrorまたはnotrunc要求され、データは、各入力ブロックから返される個別の出力ブロックとして書き込まれなければなりません。場合read()戻るフルブロックよりも少ないとsync、変換が指定されていない、結果として得られる出力ブロックは、入力ブロックと同じサイズでなければなりません。

したがって、これがおそらくあなたの混乱の原因です。はい、ためがddされて設計され、デフォルトでは、部分的に遮断するためにread()、部分的に1:sが1にマッピングされますwrite()S、または他syncにテールパディングNULまたはスペース文字に出日間bs=時にサイズconv=sync指定されています。

これは、手段ddである(これは部分的な読み取りまたは書き込みの破損のリスクなし/ w)のコピーデータに使用するのに安全なすべての場合に、それを任意により制限される一count=別段ので、引数dd喜んであろうwrite()同じサイズのブロックで、その出力入力が完全に完了するread()まで入力があったものread()に。さらには、この注意点はある唯一の真の時にbs=指定されているか、obs=されないスペックの状態では非常に次の文のように、指定されました:

  • 場合bs=exprオペランドを指定し、又は以外変換されていないsyncnoerrorまたはnotrunc要求され、入力が処理されなければならないフルサイズの出力ブロックに集め入力の終わりに到達するまで。

ibs=および/またはobs=引数がなければ、これは問題になりません- ibsobsは両方ともデフォルトで同じサイズであるためです。ただし、(優先するため)を指定せずに、いずれかに異なるサイズを指定することにより、入力バッファリングについて明示的に取得できますbs=

たとえば、次の場合:

IN| dd ibs=1| OUT

...次に、POSIX ddwrite()、すべての単一バイトを単一の出力ブロックに収集することにより、512バイトのチャンクになりますread()

そうでなければ、あなたが...

IN| dd obs=1kx1k| OUT

... POSIX ddは一度にread() 最大 512バイトですが、フルサイズの出力ブロックに入力を収集することにより、write()メガバイトサイズのすべての出力ブロック(EOFであるため、おそらく最後のカーネルを許可および除外します)

ただし、仕様からも:

  • count=n
    • n個の入力ブロックのみをコピーします。

count=i?bs=ブロックにマップするため、count=移植性のある任意の制限を処理するには、2が必要ddです。2つddのsでそれを行う最も実用的な方法は、一方の出力を別の入力にパイプすることです。これにより、元の入力タイプに関係なく、確実に特殊ファイルの読み取り/書き込みの領域に入れられます

IPCパイプと[io]bs=は、安全に指定するために、システムで定義されたPIPE_BUF制限内にそのような値を維持する必要がある引数を指定することを意味します。POSIXは、システムカーネルがで定義されている範囲内のatomic read()write()s のみを保証する必要があると述べています。POSIXは、それは保証すること、少なくとも ...PIPE_BUFlimits.hPIPE_BUF

  • {_POSIX_PIPE_BUF}
    • パイプへの書き込み時にアトミックであることが保証される最大バイト数。
    • 値:512

... (これはデフォルトのddI / Oブロックサイズでもあります)が、実際の値は通常少なくとも4kです。最新のLinuxシステムでは、デフォルトで64kです。

したがって、ddプロセスをセットアップするときは、次の3つの値に基づいたブロックファクターで実行する必要があります。

  1. bs =(obs = PIPE_BUF以下)
  2. n =読み取った合計バイト数
  3. カウント= n / bs

好む:

yes | dd obs=1k | dd bs=1k count=10k of=/dev/null
10240+0 records in
10240+0 records out
10485760 bytes (10 MB) copied, 0.1143 s, 91.7 MB/s

ddシークできない入力を処理するには、i / ow /を同期する必要があります。言い換えれば、パイプバッファを明示的にすると、問題がなくなります。それddが目的です。ここでの不明な量はyesのバッファサイズです-ただし、既知の量に他の量をブロックする場合dd、少し情報に基づいた乗算でdd データのコピーに安全に使用できます(部分的な読み取りまたは書き込みによる破損のリスクなし)count=任意のPOSIXシステムで任意の入力タイプを使用して任意の入力を任意に制限する場合でも、1バイトが欠落することはありません。

POSIX仕様の抜粋を次に示します。

  • ibs=expr
    • 入力ブロックサイズをバイト単位で指定します(デフォルトは512)expr
  • obs=expr
    • 出力ブロックサイズをバイト単位で指定します(デフォルトは512)expr
  • bs=expr
    • 両方の入力と出力ブロックサイズを設定しexpr、バイト取って代わるibs=obs=。より変換なし他の場合syncnoerrorおよびnotrunc指定され、各入力ブロックはショートブロックを集約することなく、単一のブロックとして出力にコピーされなければなりません。

また、ここで説明されているものもあります。


5

ソケット、パイプ、またはttyでは、read()およびwrite()は要求されたサイズよりも小さいサイズで転送できるため、これらでddを使用する場合は、fullblockフラグが必要です。ただし、通常のファイルとブロックデバイスでは、短い読み取り/書き込みができるのは2回だけです。EOFに達したとき、またはエラーが発生したときです。これが、fullblockフラグのないddの古い実装がディスク複製に安全に使用できる理由です。


それは現代のすべての大学に当てはまりますか?(おそらく2.0.xまたは2.2.xまでのLinuxには当てはまらなかったことを知っています。2のべき乗以外のサイズ(3kB IIRC)で呼び出され、カーネルが丸められたmke2fsため、黙って失敗write()します。 2の累乗まで)
ジル「SO-悪であるのをやめ

まったく別の問題のように聞こえる@Gilles。ブロックデバイスでは常に適切なブロックサイズの倍数を使用する必要があります。それはすべてのユニシスに当てはまると確信しており、Windowsにも当てはまります。
-psusi

テープは別として、デバイスのブロックサイズは、カーネルが純粋に気にするかどうかです。cat </dev/sda >/dev/sdbディスクのクローンを作成するのにうまく機能します。
ジル 'SO-悪である停止

OrbWeaverが答えで指摘したように、catは適切なブロックサイズを使用するためです。
-psusi

いいえ、「適切なブロックサイズ」はありません。catパフォーマンスのためにバッファサイズを選択します。カーネルからデバイス関連の情報を取得しません。別にテープから、することができますread()し、write()任意のサイズのブロックデバイスへ。少なくともLinuxではst_blksize、ブロックデバイスのiノードがあるファイルシステムのみに依存し、基盤となるデバイスには依存しません。
ジル 'SO-悪であるのをやめる'
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.