从Linux内核中获取真随机数
内核随机数产生器
Linux内核实现了一个随机数产生器,从理论上说这个随机数产生器产生的是真随机数。与标准C库中的rand(),srand()产生的伪随机数不同,尽管伪随机数带有一定的随机特征,但这些数字序列并非统计意义上的随机数。也就是说它们是可重现的--只要每次使用相同的seed值,就能得到相同的伪随机数列。通常通过使用time()的返回值来改变seed,以此得到不同的伪随机数序列,但time()返回值的结果并不是不确定的(可预测),也就是这里仍然缺少一个不确定的噪声源。对于需要真随机数的程序,都不能允许使用伪随机数。
为了获得真正意义上的随机数,需要一个外部的噪声源。Linux内核找到了一个完美的噪声源产生者--就是使用计算机的人。我们在使用计算机时敲击键盘的时间间隔,移动鼠标的距离与间隔,特定中断的时间间隔等等,这些对于计算机来讲都是属于非确定的和不可预测的。虽然计算机本身的行为完全由编程所控制,但人对外设硬件的操作具有很大的不确定性,而这些不确定性可以通过驱动程序中注册的中断处理例程(ISR)获取。内核根据这些非确定性的设备事件维护着一个熵池,池中的数据是完全随机的。当有新的设备事件到来,内核会估计新加入的数据的随机性,当我们从熵池中取出数据时,内核会减少熵的估计值。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | asmlinkage inthandle_IRQ_event(unsigned intirq, structpt_regs *regs,                             structirqaction *action){    intstatus = 1;    intretval = 0;        if(!(action->flags & SA_INTERRUPT))        local_irq_enable();    do    {        status |= action->flags;        retval |= action->handler(irq, action->dev_id, regs);        action = action->next;    }while(action);        if(status & SA_SAMPLE_RANDOM)        add_interrupt_randomness(irq);    local_irq_disable();    returnretval;} | 
上面这段代码是x86上用来处理某条中断线上注册的ISR例程的函数。这里我们感兴趣的地方是:如果ISR在注册期间指定了SA_SAMPLE_RANDOM标志,在处理完action后,还要调用add_interrupt_randomness()这个函数,它使用中断间隔时间为内核随机数产生器产生熵。内核就是在这里为熵池填充新数据的。
如果我们完全不操作计算机会如何呢?也就是作为噪声源的产生者,我们完全不去碰键盘,鼠标等外设,不让熵池获得新的数据,这个时候如果去熵池取数据内核会如何反应?
内核在每次从熵池中取数据后都会减少熵的估计值,如果熵估计值等于0了,内核此时可以拒绝用户对随机数的请求操作。
获取内核随机数
有两种方法可以从熵池中获取内核随机数。一种是通过内核导出的随机数接口,另一种是通过特殊的设备文件/dev/random和/dev/urandom。下面分别讨论两种方法。
熵的输出接口
| 1 | voidget_random_bytes(void*buf, intnbytes) | 
该函数返回长度为nbytes字节的缓冲区buf,无论熵估计是否为0都将返回数据。使用这个函数时需要在内核空间。我们写一个小模块来测试一下。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#define NUM 10voidget_random_bytes(void*buf, intnbytes);staticintget_random_number(void){    unsigned longrandNum[10];    inti = 0;    printk(KERN_ALERT "Get some real random number.\n");    for(i=0; i<NUM; i++)    {        get_random_bytes(&randNum[i], sizeof(unsigned long));        printk(KERN_ALERT "We get random number: %ld\n", randNum[i]);    }    return0;}staticvoidrandom_exit(void){    printk(KERN_ALERT "quit get_random_num.\n");}module_init(get_random_number);module_exit(random_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Test"); | 
Makefile如下:
| 1 2 3 4 5 6 7 8 9 10 | obj-m = get_random_num.oKDIR = $(shell uname -r)PWD = $(shell pwd)all:    make -C /lib/modules/$(KDIR)/build M=$(PWD) modulesclean:    make -C /lib/modules/$(KDIR)/build M=$(PWD) clean#end# | 
编译之后加载模块,通过dmesg命令输出系统log最新的信息,可以看到我们的小模块输出了10个从内核熵池中得到的随机数。卸载模块后再次加载可以重新获取新的随机数,观察输出结果,与之前得到的随机数完全不一样。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | [37972.467955] Get some real random number.[37972.468392] We get random number: -82199505[37972.468580] We get random number: -276237802[37972.468586] We get random number: 411869317[37972.468590] We get random number: 1779353222[37972.468594] We get random number: 823507551[37972.468598] We get random number: 1061461415[37972.468602] We get random number: 1372137935[37972.468606] We get random number: 1460835009[37972.468610] We get random number: 2002191729[37972.468614] We get random number: -272204344[38059.349589] quit get_random_num.[38070.575433] Get some real random number.[38070.575462] We get random number: 1111808207[38070.575476] We get random number: -13789055[38070.575481] We get random number: 240443446[38070.575485] We get random number: -606998911[38070.575489] We get random number: 538794850[38070.575493] We get random number: -500786675[38070.575497] We get random number: -1240394927[38070.575501] We get random number: 1233931345[38070.575504] We get random number: 1488497117[38070.575508] We get random number: -177688514 | 
/dev/random & /dev/urandom
这两个特殊设备都是字符型设备。我们可以在用户空间通过read系统调用读这两个设备文件以此获取随机数。这两个设备文件的区别在于:如果内核熵池的估计值为0时,
/dev/random将被阻塞,而/dev/urandom不会有这个限制。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #include <assert.h>#include <sys/stat.h>#include <sys/types.h>#include <fcntl.h>#include <unistd.h>/* 从min和max中返回一个随机值 */intrandom_number(intmin, intmax){    staticintdev_random_fd = -1;    char*next_random_byte;    intbytes_to_read;    unsigned random_value;        assert(max > min);        if(dev_random_fd == -1)    {        dev_random_fd = open("/dev/random", O_RDONLY);        assert(dev_random_fd != -1);    }        next_random_byte = (char*)&random_value;    bytes_to_read = sizeof(random_value);        /* 因为是从/dev/random中读取,read可能会被阻塞,一次读取可能只能得到一个字节,     * 循环是为了让我们读取足够的字节数来填充random_value.     */    do    {        intbytes_read;        bytes_read = read(dev_random_fd, next_random_byte, bytes_to_read);        bytes_to_read -= bytes_read;        next_random_byte += bytes_read;    }while(bytes_to_read > 0);        returnmin + (random_value % (max - min + 1));} | 
同样,还可以用dd命令从/dev/urandom中获取指定字节数的随机值并写入文件中保存--如果你需要以文件的形式提供随机数的话。
dd if=/dev/urandom of = file count = 1 bs = bytes
关于内核随机数产生器的详细介绍,可参考Linux内核设计与实现第二版附录B。
从Linux内核中获取真随机数的更多相关文章
- 从Linux内核中获取真随机数【转】
		转自:http://www.cnblogs.com/bigship/archive/2010/04/04/1704228.html 内核随机数产生器 Linux内核实现了一个随机数产生器,从理论上说这 ... 
- Linux 内核中的 Device Mapper 机制
		本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机 ... 
- Linux内核中双向链表的经典实现
		概要 前面一章"介绍双向链表并给出了C/C++/Java三种实现",本章继续对双向链表进行探讨,介绍的内容是Linux内核中双向链表的经典实现和用法.其中,也会涉及到Linux内核 ... 
- Linux内核中的fastcall和asmlinkage宏
		代码中看见:#define _fastcall 所以了解下fastcall -------------------------------------------------------------- ... 
- Linux内核中的GPIO系统之(3):pin controller driver代码分析
		一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datash ... 
- Linux内核中影响tcp三次握手的一些协议配置
		在Linux的发行版本中,都存在一个/proc/目录,有的也称它为Proc文件系统.在 /proc 虚拟文件系统中存在一些可调节的内核参数.这个文件系统中的每个文件都表示一个或多个参数,它们可以通过 ... 
- Linux内核中流量控制
		linux内核中提供了流量控制的相关处理功能,相关代码在net/sched目录下:而应用层上的控制是通过iproute2软件包中的tc来实现, tc和sched的关系就好象iptables和netfi ... 
- Linux内核中SPI/I2c子系统剖析
		Linux内核中,SPI和I2C两个子系统的软件架构是一致的,且Linux内核的驱动模型都以bus,driver,device三种抽象对象为基本元素构建起来.下文的分析将主要用这三种抽象对象的创建过程 ... 
- Linux内核中链表实现
		关于双链表实现,一般教科书上定义一个双向链表节点的方法如下: struct list_node{ stuct list_node *pre; stuct list_node *next; ElemTy ... 
随机推荐
- db2,差集
			--漏报的数据 FROM A LEFT JOIN A′ ON 交集的条件 WHERE A′.xx IS NULL --多报的数据 FROM A′ LEFT JOIN A ON 交集的条件 WHERE ... 
- Tomcat处理HTTP请求原理
			一.Tomcat是什么? Tomcat是一个Web应用服务器,同时也是一个Servlet/JSP容器.Tomcat作为Servlet容器,负责处理客户端请求,把请求传送给Servlet,并将Servl ... 
- 分享大牛开发经验,浅谈java程序员职业规划
			在中国有很多人都认为IT行为是吃青春饭的,如果过了30岁就很难有机会再发展下去!其实现实并不是这样子的,在下从事.NET及JAVA方面的开发的也有8年的时间了,在这...... 在中国有很多人都认为I ... 
- 快捷搜索框(UISearchBar)简单实现 swift
			1.在故事板里面拖入一个搜索栏和一个的tableView.2.创建的ViewController,实现代理:UISearchBarDelegate,UITableViewDataSource,的UIT ... 
- CC3200 TI 笔记
			I2C I2C总线是由Philips公司开发的一种简单.双向二线制同步串行总线.它只需要两根线即可在连接于总线上的器件之间传送信息. I2S I2S(Inter-IC Sound)总线, 又称 集成电 ... 
- 1004. 成绩排名 (20) (快速排序qsort函数的使用问题)
			读入n名学生的姓名.学号.成绩,分别输出成绩最高和成绩最低学生的姓名和学号. 输入格式:每个测试输入包含1个测试用例,格式为 第1行:正整数n 第2行:第1个学生的姓名 学号 成绩 第3行:第2个学生 ... 
- bzoj3304[Shoi2005]带限制的最长公共子序列 DP
			题意:给出三个序列,求出前两个的公共子序列,且包含第三个序列,要求长度最长. 这道题目怎么做呢,f[i][j]表示a串1-i,b串1-j的最长,g[i][j]表示a串i-n,b串j-m最长, 那么只需 ... 
- 删除右键open foler as pycharm project(WIN10)
			1.打开注册表(WIN+R 输入regedit) 2.找到 HKEY_CLASSES_ROOT\Directory\Background 路径 下找到Parcharm文件夹,删除,右键的open fo ... 
- UIButton图片文字位置的四种情况
			我们在做项目的过程中经常会遇到各定制UIButton 1.左边图片,右边文字 2.左边文字,右边图片 3.上边图片,下边文字 4.上边文字,下边图片 针对这四种情况 使用UIButton的catego ... 
- MongoDB小结12 - update【多文档更新】
			当一次更新一个文档无法满足我们的脚步时,我们可以选择一次更新多个文档,及在update的第四个参数的位置添上true,及做多文档更新,建议就算不做多文档更新也显式的在第四个参数上置false,这样明确 ... 
