STDINから指定されたバイト数を読み取る必要があるCGIを書きたいのですが。私の考えはこのようにすることです:
dd bs=$CONTENT_LENGTH count=1
しかし、ブロックサイズがRAM以外の何かによって制限されているのではないかと思っていました。
$ dd bs=1000000000000
dd: memory exhausted by input buffer of size 1000000000000 bytes (931 GiB)
GNUのcoreutilsのマニュアルページには、制限はありません。
dd
のPOSIX仕様 最大値を明示的に指定しませんが、いくつかの制限があります。
size_t
であると期待できます。これは、与えられた読み取りバイト数の型であるためです the read
関数 ;read
も SSIZE_MAX
の制限を持つように指定されています。read
は最大2,147,479,552バイトのみを転送します とにかく。64ビットプラットフォームでは、size_t
は64ビットの長さです。さらに、これは符号なしなので、2より大きい値を指定するとdd
は失敗します64 – 1:
$ dd if=/dev/zero of=/dev/null bs=18446744073709551616
dd: invalid number: ‘18446744073709551616’
Linux on 64ビットx86では、SSIZE_MAX
は0x7fffffffffffffffL(echo SSIZE_MAX | gcc -include limits.h -E -
を実行して確認)であり、これが入力制限です。
$ dd if=/dev/zero of=/dev/null bs=9223372036854775808
dd: invalid number: ‘9223372036854775808’: Value too large for defined data type
$ dd if=/dev/zero of=/dev/null bs=9223372036854775807
dd: memory exhausted by input buffer of size 9223372036854775807 bytes (8.0 EiB)
受け入れられる値を見つけたら、dd
がバッファーを読み取る前にバッファーを割り当てる必要があるため、次の制限は割り当て可能なメモリの量です。
割り当て可能な値が見つかったら、read
制限(Linuxおよび同様の制限がある他のシステム)に達します GNU dd
を指定し、iflag=fullblock
を指定します。
$ dd if=/dev/zero of=ddtest bs=4294967296 count=1
0+1 records in
0+1 records out
2147479552 bytes (2.1 GB, 2.0 GiB) copied, 38.3037 s, 56.1 MB/s
(dd
は2のすぐ下にコピーされました31 バイト、つまり上記のLinuxの制限、私が要求したものの半分でさえありません)。
上記のQ&Aで説明されているように、fullblock
の値が1より大きい場合でも、すべての入力データを確実にコピーするには、bs
が必要です。
最大値に関係なく、大きな問題があります。 POSIX仕様から:
dd
ユーティリティは、指定された入力ファイルを指定された出力ファイルにコピーし、特定の入力および出力ブロックサイズを使用して可能な変換を行います。指定された入力ブロックサイズを使用して、一度に1ブロックずつ入力を読み取ります。次に、実際に返されたデータのブロックを処理しますこれは、要求されたブロックサイズよりも小さくなる可能性があります。
(強調を追加)
私が過去に書いたように 、dd
は非常に愚かなツールです。あなたの場合、それは本質的に
_char *buf = malloc(bs);
for(int i = 0; i < count; ++i) {
int len = read(STDIN_FILENO, buf, bs);
if(len == 0) break;
write(STDOUT_FILENO, buf, len);
}
free(buf);
_
bs
はdd
がread(2)
syscallを実行するために使用する引数ですが、read(2)
は「短い読み取り」、つまり要求より少ないバイト。確かに、要求されたバイト数がすべてではない場合でも、現在使用可能なバイトがある場合は、それが実行されます。これは、入力ファイルがtty、パイプ、またはソケットの場合に一般的です(そのため、CGIで特に危険にさらされています...)。ちょうど試して:
_$ dd bs=1000 count=1
asd
asd
0+1 records in
0+1 records out
4 bytes copied, 1.75356 s, 0.0 kB/s
_
ここでasd
と入力してEnterキーを押しました。 dd
読み取り(単一のread(STDIN_FILENO, buf, 1000)
を実行して書き込んだ;要求どおりに1つのread
を実行したため、終了する。1000バイトをコピーしたようには見えない。
結局のところ、単純な「標準」dd
は、ほとんどのニーズにとってあまりにも愚かなツールです。次のいずれかを実行して、必要なことを実行できます。
bs=1
_を使用し、バイト数にcount
を使用する。これは、必要なバイト数をコピーすることが保証されています(EOFの前に利用可能な場合)。ただし、バイトごとに1つのシステムコールを実行するため、非常に非効率的です。fullblock
フラグを追加します。これにより、書き出す前にdd
が入力ブロック全体を確実に蓄積します。ただし、これは非標準であることに注意してください(GNU ddにはありますが、IDKには他のものがあります)。最終的に、POSIX以外の拡張機能を使用する場合は、_head -c
_を使用することをお勧めします。これにより、適切なバッファリングが行われ、特定のサイズ制限がなくなり、正確さと優れたパフォーマンスが保証されます。
最大値は、システム(その割り当てポリシーを含む)および現在使用可能なメモリによって異なります。
一度にすべてを読み取ろうとする代わりに(メモリが使い果たされ、スワップが原因で速度が低下する可能性があります。実際に機能するかどうかを確認するためのチェックを追加する必要があります...)dd
を使用して妥当なサイズのブロックを読み取ることができます。
それらのバイトを読み取り、ファイルに入れたいとしましょう。 bashでは、次のように実行できます(合計バイト数は$ totalにあります)。
block=65535
count=$(expr $total / $block)
rest=$(expr $total % $block)
(dd bs=$block count=$count;dd bs=$rest count=1) > filename