dd 的 bs 参数的最大值是多少?

cev*_*ing 5 dd

我想编写一个 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没有明确指定最大值,但有一些限制:

在 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 的任何值,您都需要可靠地复制所有输入数据。


Mat*_*lia 6

不管它的最大值是多少,你都有一个更大的问题;来自 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:它将通过合理的缓冲和没有特定的大小限制来做正确的事情,确保正确性和良好的性能。

  • 是的,这是这里最重要的一点。如果您想确保在一个 `read()` 系统调用中读取数据,则只能使用 `dd`,这在 OP 的 *CGI* 上下文中没有多大意义。可能值得指出的是,如果 CGI 从客户端获取任何输入,则永远不应该将它们编写为 shell 脚本,因为很难避免那里的漏洞。 (3认同)
  • @ceving:……你有什么收获?更复杂的代码和原始代码一样脆弱?只需使用`head -c`,它简洁且在所有情况下都能正常工作。 (2认同)