我想编写一个 CGI,它必须从 STDIN 读取指定数量的字节。我的想法是这样做:
dd bs=$CONTENT_LENGTH count=1
Run Code Online (Sandbox Code Playgroud)
但我想知道,块大小是否受 RAM 以外的任何其他限制。
$ dd bs=1000000000000
dd: memory exhausted by input buffer of size 1000000000000 bytes (931 GiB)
Run Code Online (Sandbox Code Playgroud)
GNU 的 coreutils 的手册页没有指定任何限制。
Ste*_*itt 24
POSIX 规范dd
没有明确指定最大值,但有一些限制:
size_t
,因为这是字节给要读取的数的类型的read
功能;read
还指定了一个限制SSIZE_MAX
;read
最多只能传输 2,147,479,552 个字节。在 64 位平台上,size_t
长度为 64 位;此外,它是无符号的,因此dd
当给定的值大于 2 64 – 1时会失败:
$ dd if=/dev/zero of=/dev/null bs=18446744073709551616
dd: invalid number: ‘18446744073709551616’
Run Code Online (Sandbox Code Playgroud)
在 64 位 x86 上的 Linux 上,SSIZE_MAX
是 0x7ffffffffffffffffL(运行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)
Run Code Online (Sandbox Code Playgroud)
一旦找到一个被接受的值,下一个限制就是可以分配的内存量,因为dd
需要先分配一个缓冲区才能读入它。
一旦找到可以分配的值,就会达到read
限制(在 Linux 和其他具有类似限制的系统上),除非您使用 GNUdd
并指定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
Run Code Online (Sandbox Code Playgroud)
(dd
复制不到 2 31个字节,即上面提到的 Linux 限制,甚至不是我要求的一半)。
如上面链接的问答中所述,fullblock
在任何情况下,对于bs
大于 1 的任何值,您都需要可靠地复制所有输入数据。
不管它的最大值是多少,你都有一个更大的问题;来自 POSIX 规范:
该
dd
实用程序应将指定的输入文件复制到指定的输出文件,并使用特定的输入和输出块大小进行可能的转换。它应使用指定的输入块大小一次读取一个块;然后它将处理实际返回的数据块,它可能小于请求的块大小。
(强调)
正如我在过去写的,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);
Run Code Online (Sandbox Code Playgroud)
bs
只是dd
用于执行read(2)
系统调用的参数,但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
Run Code Online (Sandbox Code Playgroud)
在这里我输入asd
并按回车键;dd
读取它(执行一个read(STDIN_FILENO, buf, 1000)
并写出它;它read
按照要求做了一个,所以它退出了。它看起来不像复制了 1000 个字节。
最终,简单的“标准”dd
对于大多数需求来说是一种太愚蠢的工具;您可以通过以下任一方式对其进行调整以执行所需的操作:
bs=1
和使用count
字节数;这保证复制您需要的字节数(如果在 EOF 之前可用),但效率很低,因为它每个字节执行一个系统调用;fullblock
标志;这确保dd
在写出之前累积一个完整的输入块。但是请注意,这是非标准的(GNU dd 有,IDK 有其他的)。最终,如果您要进行非 POSIX 扩展,我的建议是只使用head -c
:它将通过合理的缓冲和没有特定的大小限制来做正确的事情,确保正确性和良好的性能。
归档时间: |
|
查看次数: |
4999 次 |
最近记录: |