dd 正在生成一个 32 MB 的随机文件而不是 1 GB

Tri*_*tos 51 script dd random-number-generator

我想生成一个 1 GB 的随机文件,所以我使用了以下命令。

dd if=/dev/urandom of=output bs=1G count=1
Run Code Online (Sandbox Code Playgroud)

但是每次我启动这个命令时,我都会得到一个 32 MB 的文件:

<11:58:40>$ dd if=/dev/urandom of=output bs=1G count=1
0+1 records in
0+1 records out
33554431 bytes (34 MB, 32 MiB) copied, 0,288321 s, 116 MB/s
Run Code Online (Sandbox Code Playgroud)

怎么了?

编辑:

感谢本主题中的出色答案,我提供了读取 32 个 32 MB 大块的解决方案,这使得 1GB:

dd if=/dev/urandom of=output bs=32M count=32
Run Code Online (Sandbox Code Playgroud)

给出了其他解决方案,将 1 GB 直接读取到内存,然后写入磁盘。此解决方案需要大量内存,因此不推荐使用:

dd if=/dev/urandom of=output bs=1G count=1 iflag=fullblock
Run Code Online (Sandbox Code Playgroud)

use*_*686 94

bs,缓冲区大小,表示由 dd 完成的单个read()调用的大小。

(例如,bs=1M count=1bs=1k count=1k都会产生一个 1 MiB 的文件,但第一个版本将在一个步骤中完成,而第二个将在 1024 个小块中完成。)

常规文件几乎可以在任何缓冲区大小下读取(只要该缓冲区适合 RAM),但设备和“虚拟”文件通常与单个调用非常接近,并且对它们每次生成的数据量有一些任意限制读取()调用。

对于/dev/urandom,此限制在drivers/char/random.c中的urandom_read()中定义:

#define ENTROPY_SHIFT 3

static ssize_t
urandom_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
    nbytes = min_t(size_t, nbytes, INT_MAX >> (ENTROPY_SHIFT + 3));
    ...
}
Run Code Online (Sandbox Code Playgroud)

这意味着每次调用该函数时,它都会将请求的大小限制为 33554431 字节。

默认情况下,与大多数其他工具不同,dd在接收到的数据少于请求的数据后不会重试——您将获得 32 MiB,仅此而已。(要使其自动重试,如 Kamil 的回答,您需要指定iflag=fullblock.)


另请注意,“单个 read() 的大小”意味着整个缓冲区必须一次适合内存,因此大量块大小也对应于dd 的大量内存使用。

而且这一切都是毫无意义的,因为当超过 ~16–32 MiB 块时通常不会获得任何性能 - 系统调用不是这里的慢部分,随机数生成器才是。

因此,为简单起见,只需使用head -c 1G /dev/urandom > output.

  • _“...当超过 ~16-32 MiB 块时,通常不会获得任何性能”_ - 根据我的经验,在 64-128 **kilo**byte 以上时,您往往不会获得太多性能,甚至会失去性能. 在这一点上,您在系统调用成本的收益递减中很好,并且缓存争用开始发挥作用。 (7认同)
  • @AndrewHenle 啊,有趣!我在我的机器上用 `dd` 做了一个快速测试,块大小从 1k 到 512M。从 Intel 750 SSD 读取数据,在 2MiB 块上实现了最佳性能(约 1300MiB/s),与您的结果大致相符。更大的块大小既没有帮助也没有阻碍。从 `/dev/zero` 读取,最佳性能(几乎 20GiB/s)在 64KiB 和 128KiB 块;较小和较大的块都会降低性能,大致符合我之前的评论。底线:以您的实际情况为基准。当然,我们都没有对 `/dev/random` 进行基准测试:P (6认同)
  • 我会明确指出`iflag=fullblock` 是[POSIX `dd` 实用程序](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/dd.html) 的GNU 扩展。由于问题没有指定 Linux,我认为应该明确指出 Linux 特定扩展的使用,以免将来试图在非 Linux 系统上解决类似问题的读者感到困惑。 (4认同)
  • @marcelm 我帮助构建了高性能系统,其中 IO 性能会随着块大小增加到 1-2 MB 块而提高,在某些情况下可达 8 MB 左右。每个 LUN。由于文件系统是使用多个并行 LUN 构建的,为了获得最佳性能,意味着使用多个线程进行 IO,每个线程执行 1 MB+ 块。持续 IO 速率超过 1 GB/秒。这些都是旋转磁盘,所以我可以看到高性能 SSD 阵列在块大小增长到 16 甚至 32 MB 块时越来越快地吞食或生成数据。容易地。也许更大。 (3认同)
  • @Xen2050 我做了一些更快速的测试,看起来 `dd` 更快。快速跟踪显示 `head` 使用 8KiB 读取和两次 4KiB 写入,这很有趣(Debian 9.6 / Linux 4.8 上的 GNU coreutils 8.26)。`head` 速度确实介于 `dd bs=4k` 和 `dd bs=8k` 之间。`head` 速度与 `dd if=/dev/zero bs=64k` 相比下降了约 40%,与 dd if=/dev/nvme0n1 bs=2M` 相比下降了约 25%。从 `/dev/zero` 读取当然更多地受 CPU 限制,但对于 SSD I/O 队列也起作用。这比我预期的要大。 (3认同)

Kam*_*ski 21

dd可以读取小于ibs(注意:bs指定两者ibsobs),除非iflag=fullblock指定。0+1 records in表示读取了0完整块和1部分块。然而,任何完整或部分块都会增加计数器。

我不知道产生的确切机制 ddread 块小于1G这种特殊情况。我猜任何块在写入之前都会被读取到内存中,因此内存管理可能会干扰(但这只是一个猜测)。编辑:此并发答案解释了使dd读取块小1G于此特定情况下的机制。

反正我不推荐这么大 bs. 我会使用bs=1M count=1024. 最重要的是:没有iflag=fullblock 任何读取尝试可能读取不到ibs(除非ibs=1,我认为,这虽然效率很低)。

因此,如果您需要读取一些确切数量的数据,请使用iflag=fullblock. 注意iflagPOSIX 不需要,您dd可能不支持它。根据这个答案 ibs=1可能是读取确切字节数的唯一 POSIX 方法。当然,如果您更改,ibs则需要重新计算count. 在您的情况下,降低ibs32M或更少可能会解决问题,即使没有iflag=fullblock.

在我的 Kubuntu 中,我会像这样修复你的命令:

dd if=/dev/urandom of=output bs=1M count=1024 iflag=fullblock
Run Code Online (Sandbox Code Playgroud)