我正在使用/dev/urandom
进行dd
,以创建具有随机内容的文件。这个方法效果很好,但是我希望能够通过再次运行相同种子的PRNG来在稍后的时间点上再现文件内容。是否有一种可与字符设备配合使用的可种子PRNG?
我正在使用最新的Linux 3.X内核。
我正在使用/dev/urandom
进行dd
,以创建具有随机内容的文件。这个方法效果很好,但是我希望能够通过再次运行相同种子的PRNG来在稍后的时间点上再现文件内容。是否有一种可与字符设备配合使用的可种子PRNG?
我正在使用最新的Linux 3.X内核。
Python 3.9 random.randbytes
+ random.seed
我已经学会了停止与Bash无法完成的事情斗争,只是顺其自然:
randbytes() (
python -c 'import random;import sys;random.seed(int(sys.argv[1]));sys.stdout.buffer.write(random.randbytes(int(sys.argv[2])))' "$@"
)
使用方法:
randbytes <seed> <nbytes>
e.g.:
randbytes 0 8 | hd
始终使用种子0输出8个相同的伪随机字节:
00000000 cd 07 2c d8 be 6f 9f 62 |..,..o.b|
00000008
可阅读的多行版本在此处:生成可种子化数据的随机字符串
在我的联想ThinkPad P51上,我可以在0.5秒内将1亿字节转储到ramfs中。然而,如果我尝试转储10亿字节,则会出现以下错误:
Python int too large to convert to C int
所以要记住这一点。
作为比较:
time sudo dd if=/dev/urandom of=ramfs/test bs=4k count=24414
花费了2.5秒,因此速度较慢,这并不奇怪,因为它是一个更随机的源,而Python生成器是确定性的,并且似乎是用C编写的。
在Ubuntu 20.10、Linux内核5.8.0上进行了测试。
/dev/urandom
旨在尽可能地不可预测。看起来您需要一个更常规的带种子的伪随机数生成器。
这是我刚写的一个C语言示例:
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char *endptr;
unsigned long int seed;
if (argc != 2 || *argv[1] == '\0') {
fprintf(stderr, "usage: %s seed\n", argv[0]);
return EXIT_FAILURE;
}
errno = 0;
seed = strtoul(argv[1], &endptr, 0);
if (errno != 0 || *endptr != '\0' || seed > UINT_MAX) {
fprintf(stderr, "%s: invalid seed\n", argv[0]);
return EXIT_FAILURE;
}
srandom((unsigned int) seed);
while (1) {
int i;
long int randomnum = random();
for (i = 0; i < sizeof randomnum; i++) {
if (putchar((randomnum >> (i * CHAR_BIT)) & UCHAR_MAX) == EOF) {
return EXIT_SUCCESS;
}
}
}
}
这是一个程序,而不是设备文件,但它的输出格式与您从/dev/urandom
获得的格式相同。您可以将其输出通过管道传输到dd
并省略if
。
如果您需要提供一个真正随机的种子来供该程序使用,可以在bash中像这样从/dev/urandom
获取:
seed=$(od -vAn -N4 -tu4 </dev/urandom)
将4替换为您的机器上的sizeof(unsigned int)
(它可能是4)。
When a Linux system starts up without much operator interaction, the entropy pool may be in a fairly predictable state. This reduces the actual amount of noise in the entropy pool below the estimate. In order to counteract this effect, it helps to carry entropy pool information across shut-downs and start-ups. To do this, add the following lines to an appropriate script which is run during the Linux system start-up sequence:
echo "Initializing kernel random number generator..." # Initialize kernel random number generator with random seed # from last shut-down (or start-up) to this start-up. Load and # then save 512 bytes, which is the size of the entropy pool. if [ -f /var/random-seed ]; then cat /var/random-seed >/dev/urandom fi dd if=/dev/urandom of=/var/random-seed count=1
/dev/*random不使用种子,因为它们不是伪随机数生成器。它们从环境中提供随机性(甚至使用硬件作为源,如键盘、中断、网络等)。在/dev/urandom的情况下,仅在池耗尽时才使用种子和RNG。但是当发生这种情况时,它仍然是不可预测的。
因此,请勿从/dev/*random读取随机数字。相反,使用bash工具生成随机数字:
#! /bin/bash
# Seed the RNG RANDOM = 1234
# Print 10 random numbers for i in {1..10} do echo $RANDOM done
上面的脚本每次都会打印相同的数字序列,因为种子是恒定的。
如果要生成字节,即0到255范围内的值,则使用printf
而不是echo
并将得到的随机数循环到该范围:
printf "\\x$(printf "%x" $(($RANDOM % 256)))"
现在运行脚本并将输出重定向到文件中,它将包含10个“随机”字节。
如果您希望使用种子生成随机文件,那么您正在使用错误的工具。/dev/urandom 从环境中获取其随机性(传入和传出网络数据包的时间、磁盘访问等),因此即使您使用相同的种子,也极不可能连续两次获得相同的随机数序列。
您需要一个传统的(也称为仅软件)伪随机数生成器。Mersenne Twister 是一个很好的选择。它应该在您使用的语言中有实现。或者只需使用您的语言自带的任何伪随机数生成器即可。