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

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

我想编写一个CGI,它必须从STDIN 读取指定数量的字节。我的想法是这样做:

dd bs=$CONTENT_LENGTH count=1

但我想知道,块大小是否受到除 RAM 之外的其他因素的限制。

$ dd bs=1000000000000
dd: memory exhausted by input buffer of size 1000000000000 bytes (931 GiB)

GNU coreutils 的手册页没有指定任何限制。

答案1

POSIX 规范dd不要明确指定最大值,但有一些限制:

在64位平台上,size_t长度为64位;此外,它是无符号的,因此dd当给定的值大于 2 64 – 1 时将会失败:

$ dd if=/dev/zero of=/dev/null bs=18446744073709551616
dd: invalid number: ‘18446744073709551616’

在 64 位 x86 的 Linux 上,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 和其他具有类似限制的系统上),除非你使用 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

dd复制了不到 2 31字节,IE上面提到的Linux限制,甚至还不到我要求的一半)。

正如上面链接的问答中所解释的,在任何情况下,对于任何大于 1 的fullblock值,您都需要可靠地复制所有输入数据。bs

答案2

不管它的最大值是多少,你都会遇到一个更大的问题;来自 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);

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

我在这里输入asd并按回车键;dd读取它(执行一次read(STDIN_FILENO, buf, 1000)并将其写出;它read按照要求执行了一次,因此退出。看起来它没有复制 1000 个字节。

最终,对于大多数需求来说,简单的“标准”dd是一种过于愚蠢的工具。您可以通过以下任一方式使其完成您需要的操作:

  • 通过使用bs=1和使用count字节数;这保证复制您需要的字节数(如果在 EOF 之前可用),但效率很低,因为它每个字节执行一个系统调用;
  • 添加fullblock标志;这可以确保dd在写出之前累积完整的输入块。但请注意,这是非标准的(GNU dd 有它,IDK 关于其他的)。

最终,如果您要使用非 POSIX 扩展,我的建议是只使用head -c:它将通过合理的缓冲做正确的事情,并且没有特定的大小限制,确保正确性和良好的性能。

答案3

最大值取决于系统(包括其分配策略)和当前可用内存。

您可以使用dd.

假设您想读取这些字节并将它们放入文件中。在 bash 中你可以运行这样的东西(总字节数在 $total 中):

block=65535
count=$(expr $total / $block)
rest=$(expr $total % $block)
(dd bs=$block count=$count;dd bs=$rest count=1) > filename

相关内容