Linux下随机数生成的常见方法
众所周知,利用Linux下的rand函数可以生成范围在0到RAND_MAX(在stdlib.h中定义,值为2147483647)的数值,但是一般来讲,为了达到更好的随机效果,需要利用srand函数设置相应的随机种子(或者说随机数的起始值),种子相同,所产生的随机数也是相同的,因此,要想获得随机效果好的随机数,一定要保证每次的随机种子有差别。常见的可作为随机种子的有:当前时间、/dev/random或/dev/urandom文件内容以及uuid码。
在阐述各种随机种子产生之前,有必要先介绍下srand函数,它是ISO C下的产生随机数的标准函数,BSD下与之相对应的函数是random。srand函数需要接收的参数是一个无符号整型数据。
一、当前时间:
利用time函数每次获取当前的系统时间,并且也保证了每次的随机种子有差异,实验证明它是一种最为简介,同时也是使用最为广泛的方法,但是,针对某些特定环境,未必会产生有效的随机种子。在本篇日志中,我们试图将这三种产生随机种子的方法应用于这样一种特殊环境:在嵌入式环境下,试图在开发板启动之后、系统时间没有同步之前这段时间去产生随机种子,并检验所产生的随机种子的有效性,值得说明的是,此种开发板不会去保存系统时间,每次启动之后都会从一个默认时间开始计时。通过实验得出,当前时间运用在这种环境下完全不能达到产生随意随机数的目的,因为嵌入式开发板的特殊性,在每次开发板运行到产生随机数的程序时,此时的当前时间都是一样的,于是每次重新启动开发板所产生的随机数也是一样的。
二、/dev/random以及/dev/urandom:
/dev/random以及/dev/urandom产生随机种子的原理是利用当前系统的熵池来计算出一定数量的随机比特,其中熵池是根据当前系统的“环境噪音”,它是由很多参数共同评估的,如内存的使用,文件使用量等等,环境噪音直接影响着所产生的随机种子的有效性。同样,若我们试图运用于前面所说的特殊环境,是否能够产生满足所需的随机种子呢?实验证明,能够保证每次开发板在每次启动之后能获取到不同的随机数。毕竟环境噪音所选取的评估参数众多,会导致所获取到的种子有很大的差异性。
下面试图阐述一下/dev/random与/dev/urandom之间的区别。根据维基百科所述,urandom即”unlocked random”,,每次打开并读取/dev/urandom时,会从熵池中随机返回所需要的字节数,/dev/urandom的读取操作不会阻塞,因为它会重复使用熵池中的数据以产生随机数;反之/dev/random则是每次读之前去检查熵池是否为空,若为空,则需要阻塞并去更新熵池。从这点可以看出,前者不需要阻塞,但是随机效果弱于后者,因此常用于弱密码系统。
相关代码如下:
#include <stdio.h>#include <string.h>#include <stdlib.h>#define FILENAME "/proc/sys/kernel/random/uuid"#define MAX_SEED 10#define UUID_LENGTH 35#define UUID_DELIM "-"void check_rand_number(char *seed, char *p){ char *tmp_seed = "2147483647"; //MAX_RAND char *tmp = seed; //fill the new number to seed array *(seed + strlen(seed)) = *p; if (strcmp(tmp_seed, tmp) >= 0) { return ; } else { *(seed + strlen(seed) - 1) = '\0'; }}void parse_uuid_and_save(FILE *fs_p, char *seed){ char uuid[UUID_LENGTH + 1] = {0}; char *p = NULL; //UUID format like as "2946b341-55b0-44c6-9710-04c947d1e3e1" if (NULL != fgets(uuid, UUID_LENGTH, fs_p)) { if ((p = strtok(uuid, UUID_DELIM)) != NULL) { while(*p != '\0') { if (*p >= '0' && *p <= '9') { *(seed + strlen(seed)) = *p; p++; } else { p++; } } } else { return; } while ( (p = strtok(NULL, UUID_DELIM)) != NULL) { while((*p != '\0') && (strlen(seed) < MAX_SEED)) { if (*p >= '0' && *p <= '9') { //when add the tenth bit, should check the number must less than RAND_MAX(2147483647) if (strlen(seed) == (MAX_SEED - 1)) { check_rand_number(seed, p); return; } else { *(seed + strlen(seed)) = *p; p++; } } else { p++; } } } }}int main(int argc, char **argv){ FILE *fs_p = NULL; char seed[MAX_SEED + 1] = {0}; fs_p = fopen (FILENAME, "r"); if (NULL == fs_p) { printf("Can not open UUID file!\n"); return -1; } else { parse_uuid_and_save(fs_p, seed); fclose(fs_p); } srand(strtol(seed, NULL, 10)); printf("Random number is %d.\n", rand()); return 0;}