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


26

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

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

但是,每次运行它时,最终都会得到一个不同大小的文件。为什么是这样?如何生成具有随机数据的给定大小的文件?


1
/dev/random如果没有足够的熵来生成所需的位数,则将阻止。收集大量高质量的伪随机“随机性”只需要花费时间...要么使用随机性/dev/urandom较低的“随机性”值,要么检查您的熵池(循环检查,然后根据需要等待)...
Peter.O 2012年

另请参阅此问题
基思·汤普森

3
只需添加iflag=fullblock
frostschutz

Answers:


31

您正在观察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())

除非您正在设计OS安装程序或克隆程序,否则永远不要/dev/random在Linux下使用/dev/urandom。该urandom手册页有所误导; /dev/urandom实际上,它适用于加密,甚至可以生成长期存在的密钥。唯一的限制/dev/urandom是必须提供足够的熵。Linux发行版通常在重新启动之间保存熵,因此,唯一可能没有足够熵的情况是全新安装。实际上,熵不会消失。有关更多信息,请阅读/ dev / urandom中的兰特对于登录密钥是否安全?喂/ dev / random熵池?

dd诸如head或的工具可以更好地表达的大多数用法tail。如果要2kB的随机字节,请运行

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


感谢您提供使用head而不是dd的提示。如果需要的话,这允许我仍然使用/ dev / random。尽管/ dev / urandom可能就足够了,但是您很高兴知道在需要时如何使用/ dev / random。
丹尼尔(Daniel)

从3.16开始,在内核上/dev/urandom read() s 返回32m
mikeserv

另外,如果您需要POSIX兼容命令,则可以在此处使用技巧:unix.stackexchange.com/a/192114 dd if=/dev/urandom ibs=1k obs=1k | dd bs=1k count=2
Rufflewind

11

man 4 random在RHEL 5盒:

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

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

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

我每次调用都会得到2048个字节 dd if=/dev/urandom of=rand bs=1K count=2

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


是的,实际上,除非他使用的是真正的加密应用程序,否则@Daniel应该使用/ dev / urandom。但是我对为什么dd if=/dev/random bs=1K count=2在熵池明显耗尽时为什么会停止感到困惑。从文档开始,它应该阻塞直到出现更多的熵为止,因此dd它将缓慢地写出文件,而不是仅转储当前池并退出。
cjc 2012年

我也想知道这一点,但是它在RHEL,Slackware 13.1和最新的Arch中是一致的。RHEL是x86_64,其他的是32位。不幸的是,dd文档是GNU信息格式,所以我还没有全部阅读。
布鲁斯·埃迪格

Gentoo上也是如此。
马修·沙利

4
@cjc:这是因为当您调用read(fd, mybuf, 1024)阻塞的FD时,它会在基础设备返回一些数据后立即返回。如果要读取1024个字节,它将返回该字节。如果只有201个字节,它将返回201。如果有0个字节,它将阻塞直到至少一个字节变为可用,然后再返回。
沃伦·杨

@WarrenYoung是否从/ dev / random中读取内容?我想是这样。
Michael Martinez

5

为什么要dd丢弃数据?... Gilles提出了一个有关dd以下问题:
dd什么时候适合复制数据?(或者,当部分为read()和write()时)
这是该问题的摘录:

    * ...将dd置于错误并不难;例如,尝试以下代码:**
        yes | dd of=out bs=1024k count=10
    并检查out文件的大小(该文件的大小很可能在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"

1

dd为阻塞而设计 -如果您需要立即完成操作,它通常是最好的工具,可用于读取可变大小的输入,因为dd这不会缓冲将来的电流读取write() (除非您使用比ibs大的obs来明确配置),而是将write()一切早点读,因为它read()的IT (以及可选的处理它)

以下是一些重要的定义

  • ibs=expr
    • 指定输入块的大小(以字节为单位)(默认值为512)expr
  • obs=expr
    • 指定输出块的大小(以字节为单位)(默认值为512)expr
  • bs=expr
    • 将输入和输出块大小均设置为expr字节,ibs=并替换为和obs=。如果没有转换以外syncnoerrornotrunc被指定时,每一个输入块应被复制到输出为单个块,而不聚集短块。

因此,您会看到,何时ibsobs一起定义时,bsibs优先-否则,如果您是特定的,则选择obscbs做。

这是ibs最重要的示例。如果要跟踪/dev/random池填充的时间,可以执行以下操作:

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

只要if=目标完全可读,它就会始终生成相同大小的输出文件,因为dd它将sync对null读取的块进行hronize。换句话说,如果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"输出块,write()直到每个输入块与第二个之间的管道为止dd。这意味着,第二dd可以可靠地限制输出count="$lmt",因为所有的write()S中的第一品牌将匹配其I / O块大小- 无论有多少read()上了第一dd必须做的是真的。

就是dd仅需一点点数学就可以用来可靠地读取管道或其他类型的特殊文件的方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.