为什么 /dev/random 中的 dd 给出不同的文件大小?

为什么 /dev/random 中的 dd 给出不同的文件大小?

我在 ubuntu 系统上运行以下命令:

dd if=/dev/random of=rand bs=1K count=2

然而,每次运行它时,我都会得到一个不同大小的文件。为什么是这样?如何生成充满随机数据的给定大小的文件?

答案1

您正在观察 的特殊行为dd与 Linux 的特殊行为的组合/dev/random。顺便说一句,两者都不是完成这项工作的正确工具。

Linux/dev/random很少返回数据。它基于伪随机数生成器中的熵以非常快的速度消失的假设。由于收集新熵的速度很慢,/dev/random因此通常一次仅放弃几个字节。

dd是一个古老的、古怪的程序,最初旨在在磁带设备上运行。当您告诉它读取 1kB 的一个块时,它会尝试读取一个块。如果读取返回的数据少于 1024 个字节,那么你只能得到这些了。所以dd if=/dev/random bs=1K count=2打了两个read(2)电话。由于它是从 读取的/dev/random,因此这两个read调用通常仅返回几个字节,其数量取决于可用的熵。也可以看看dd什么时候适合复制数据? (或者,什么时候 read() 和 write() 是部分的)

除非您正在设计操作系统安装程序或克隆程序,否则您永远不应该/dev/random在 Linux 下使用/dev/urandom。手册urandom页有些误导;/dev/urandom实际上适用于密码学,甚至可以生成长期密钥。唯一的限制/dev/urandom是它必须提供足够的熵; Linux 发行版通常会在重新启动之间保存熵,因此唯一一次可能没有足够熵的情况是在全新安装时。实际上,熵不会消失。欲了解更多信息,请阅读/dev/urandom 中的 rand 对于登录密钥来说是否安全?喂养/dev/随机熵池?

的大多数用法可以通过或dd等工具更好地表达。如果您想要 2kB 的随机字节,请运行headtail

head -c 2k </dev/urandom >rand

使用较旧的 Linux 内核,您可以逃脱

dd if=/dev/urandom of=rand bs=1k count=2

因为/dev/urandom很高兴返回了所请求的尽可能多的字节。但从内核 3.16 开始就不再如此了,现在限制为 32MB

一般来说,当您需要dd提取固定数量的字节并且其输入不是来自常规文件或块设备时,您需要逐字节读取:dd bs=1 count=2048

答案2

man 4 randomRHEL 5 盒子上:

读取时,/dev/random 设备将仅返回熵池中估计的噪声位数内的随机字节。

我在那台机器上获得大小为 213 字节的文件。回到男人4随机:

读取时,/dev/urandom 设备将返回请求的字节数。

我从每次调用中获得 2048 个字节dd if=/dev/urandom of=rand bs=1K count=2

我的结论是,差异是由于您的机器在调用之间生成了多少熵dd if=/dev/random ...

答案3

为什么会dd丢失数据? ...吉尔斯提出了这个引人入胜的问题dd
dd什么时候适合复制数据? (或者,什么时候 read() 和 write() 是部分的)
这是该问题的摘录:

    *...将 dd 归咎于错误并不难;例如尝试以下代码:**
        yes | dd of=out bs=1024k count=10
    并检查输出文件的大小(可能远低于 10MB)。


除了我的评论(在你的问题末尾)之外,类似这样的东西值得观看......它捕获 file 中的字节$trnd。我半任意地选择了 bs=8

移动鼠标并观察它的速度。
当我的计算机空闲时(AFK并且没有网络活动),并且在耗尽熵池之后,2小时12分钟仅收集第1192章字节,此时我取消了它。

然后,随着我不断移动鼠标,花费的时间相对短得多1分钟15秒收集相同数量的字节。

这非常清楚地表明收集熵不是基于 CPU 速度的,而是基于随机事件基于,并且我的 Ubuntu 系统使用鼠标作为其之一重要的随机因素。

get=2048
trnd=/tmp/$USER.rnd; >"$trnd"
while (( $(wc -c <"$trnd") < $get )) ;do
    dd if=/dev/random bs=8 count=1 2>/dev/null >>"$trnd"
    echo -n "itt: $((i+=1))  ct: "; wc -c <"$trnd"
done
truncate -s $get "$trnd"
echo -e "\nfinal count: "; wc -c <"$trnd"

答案4

dd设计的用于阻塞 - 如果您需要的话,它通常是您可以使用的从可变大小的输入中读取数据的最佳工具立即地因为dd不会将当前读取缓冲到将来write() (除非你非常明确地使用比 ibs 更大的 obs 来配置它),但会write()立即读取它所读取的所有read()内容(并可选择对其进行处理)

以下是一些重要的定义:

  • ibs=expr
    • 指定输入块大小(以字节为单位)expr (默认为 512)
  • obs=expr
    • 指定输出块大小(以字节为单位)expr (默认为 512)
  • bs=expr
    • 将输入和输出块大小设置为expr字节,取代ibs=obs=.如果除syncnoerror、 和之外未notrunc指定任何转换,则每个输入块应作为单个块复制到输出,而不聚合短块。

所以你看,当ibsobs一起定义时, bsthenibs优先 - 但除此之外,如果你是具体的,那么要么obs要么cbs

这是一个最重要的例子ibs。如果你想追踪游泳池多久才会/dev/random充满,你可以做这样的事情……

dd "ibs=$size" conv=sync "count=$lmt" \ 
    if=/dev/random of="$somefile"

只要if=的目标是可读的,那就会总是导致相同大小的输出文件,因为hronizeddsync阻止对空值的读入。换句话说,如果dd read()s 代表时间的输入块$((size=10)) $((count=5))并且read()文件返回 2 个字节,然后是 8 个字节,然后是 12 个字节,然后是 2 个字节,然后是 4 个字节,dd将写入其输出文件,类似于

 2 read bytes 8NULs \
 8 read bytes 2NULs \
10 read bytes 0NULs \
 4 read bytes 6NULs \
 4 read bytes 6NULs

...因为dd默认情况下,不是延迟。因此,如果您需要跟踪流内并分隔其他进程的写入,dd那么该工具适合您。

如果您只是将一定量的数据写入常规文件,那么与此处所做的其他陈述相反,您也可以用于dd此目的 - 并且相当容易 - 但您将需要多个数据和一个可靠的阻塞因素

例如,如果您这样做:

{   dd ibs="$size" obs="${size}x$block_factor" |
    dd bs="${size}x$blockfactor" "count=$lmt"
}  <infile >outfile

...第一个dd将缓冲尽可能多的输入块,以便为它和第二个之间的管道ibs="$size"填充至少一个obs="${size}x$block_factor"输出块。这意味着第二个可以可靠地限制输出,因为第一个生成的所有s 将与其 i/o 块大小匹配 -write()ddddcount="$lmt"write()不管read()第一个必须做多少dd才能做到这一点。

这就是您如何dd可靠地读取管道或其他类型的特殊文件 - 只需一点数学知识。

相关内容