我想编写一个 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:它将通过合理的缓冲和没有特定的大小限制来做正确的事情,确保正确性和良好的性能。