我想编写一个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
不要明确指定最大值,但有一些限制:
- 用于存储给定值的数据类型预计是
size_t
,因为这是要读取的字节数的类型功能read
; read
还指定有一个限制SSIZE_MAX
;- 在Linux下,
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’
在 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