好的。所以首先,让我明确一点,libc 中的 PRNG 是确定性的(这就是它想要种子的原因),使用LCG - 这意味着一旦你有几个值就很容易预测所有值,因此不安全。
那么现在。 erand48() 从伪随机实数的均匀分布中返回大小为 double 的随机浮点值。它本身不需要种子值,而是需要您提供状态缓冲区。这是完整的声明:
double erand48(unsigned short xsubi[3]);
有趣的是,状态缓冲区必须以随机值作为种子,以使生成器工作。我的第一个想法是阅读/dev/urandom。
我们可以用这样的方法来做到这一点(使用无缓冲读取来防止这种小读取造成的浪费):
#include <stdio.h>
#include <stdlib.h>
void *thread_f (void *i) {
// setup unbuffered urandom
urandom = fopen ("/dev/urandom", "r");
setvbuf (urandom, NULL, _IONBF, 0); // turn off buffering
// setup state buffer
unsigned short randstate[3];
// fgetc() returns a `char`, we need to fill a `short`
randstate[0] = (fgetc (urandom) << 8) | fgetc (urandom);
randstate[1] = (fgetc (urandom) << 8) | fgetc (urandom);
randstate[2] = (fgetc (urandom) << 8) | fgetc (urandom);
// cleanup urandom
fclose (urandom);
// you can now use erand48 (randstate);
... // do whatever work you need to do
return result;
}
这是线程安全的,甚至可以确保所有线程的种子值相对安全。
当然,如果速度不是太大的问题(即:你可以忍受一点速度损失)并且你可以忍受整数,那么直接从/dev/urandom 进行无缓冲读取是一个完美的解决方案。更好的是,/dev/urandom 提供了安全的、不可预测的伪随机整数(从技术上讲,是一个字节流,但只要您匹配大小,它们将始终作为整数工作)通常也是均匀分布的。
另外,/dev/urandom 会定期向其中注入熵并刷新,确保您拥有相当数量的相当随机数。