从Linux内核中获取真随机数【转】
转自:http://www.cnblogs.com/bigship/archive/2010/04/04/1704228.html
内核随机数产生器
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 int handle_IRQ_event(unsigned int irq, struct pt_regs *regs,
struct irqaction *action)
{
int status = 1;
int retval = 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();
return retval;
}
|
上面这段代码是x86上用来处理某条中断线上注册的ISR例程的函数。这里我们感兴趣的地方是:如果ISR在注册期间指定了SA_SAMPLE_RANDOM标志,在处理完action后,还要调用add_interrupt_randomness()这个函数,它使用中断间隔时间为内核随机数产生器产生熵。内核就是在这里为熵池填充新数据的。
如果我们完全不操作计算机会如何呢?也就是作为噪声源的产生者,我们完全不去碰键盘,鼠标等外设,不让熵池获得新的数据,这个时候如果去熵池取数据内核会如何反应?
内核在每次从熵池中取数据后都会减少熵的估计值,如果熵估计值等于0了,内核此时可以拒绝用户对随机数的请求操作。
获取内核随机数
有两种方法可以从熵池中获取内核随机数。一种是通过内核导出的随机数接口,另一种是通过特殊的设备文件/dev/random和/dev/urandom。下面分别讨论两种方法。
熵的输出接口
1
|
void get_random_bytes( void *buf, int nbytes)
|
该函数返回长度为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 10
void get_random_bytes( void *buf, int nbytes);
static int get_random_number( void )
{
unsigned long randNum[10];
int i = 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]);
}
return 0;
}
static void random_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.o
KDIR = $(shell uname -r)
PWD = $(shell pwd)
all:
make -C /lib/modules/$(KDIR)/build M=$(PWD) modules
clean:
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中返回一个随机值 */
int random_number( int min, int max)
{
static int dev_random_fd = -1;
char *next_random_byte;
int bytes_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
{
int bytes_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);
return min + (random_value % (max - min + 1));
}
|
同样,还可以用dd命令从/dev/urandom中获取指定字节数的随机值并写入文件中保存--如果你需要以文件的形式提供随机数的话。
dd if=/dev/urandom of = file count = 1 bs = bytes
关于内核随机数产生器的详细介绍,可参考Linux内核设计与实现第二版附录B。
本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/4551645.html,如需转载请自行联系原作者
从Linux内核中获取真随机数【转】相关推荐
- Linux内核中获取纳秒时间戳的方法
Linux内核中获取纳秒时间戳的方法 1 方法1:使用getnstimeofday64方法 2 方法2:使用ktime_get_real_ns方法 1 方法1:使用getnstimeofday64方法 ...
- linux内核中获取虚拟地址api,Linux内核-系统调用
Linux内核-系统调用 1.与内核通信 #系统调用在用户空间进程和硬件设备之间添加了一个中间层 作用:1.为用户空间提供了一种硬件的抽象接口 2.系统调用保证了系统的稳定和安全 3.出于每一个进程都 ...
- linux内核中测量时间的方法,Linux内核中获取时间函数do_gettimeofday
内核代码能一直获取一个当前时间的表示, 通过查看 jifies 的值. 常常地, 这个值只代表从最后一次启动以来的时间, 这个事实对驱动来说无关, 因为它的生命周期受限于系统的 uptime. 如所示 ...
- 论文中文翻译——Double-Fetch情况如何演变为Double-Fetch漏洞:Linux内核中的双重获取研究
本论文相关内容 论文下载地址--Web Of Science 论文中文翻译--How Double-Fetch Situations turn into Double-Fetch Vulnerabil ...
- linux 内核 遍历文件夹,Linux内核中通过文件描述符获取绝对路径
背景 在Linux内核中,已知一个进程的pid和其打开文件的文件描述符fd,如何获取该文件的绝对路径?基本思路是先获取该文件在内核中的file结构体,再通过d_path()获取到整个文件的绝对路径. ...
- linux内核中锁有哪些,Linux内核中有哪些锁
Linux内核中的各种锁 在LInux操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实象多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问.尤其是在多处理器系统上,更需 ...
- Linux内核中锁机制之完成量、互斥量
在上一篇博文中笔者分析了关于信号量.读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题. 八.完成量 下面讨论完成量的内容,首先需明确完成量表示为一个执行单元需要等 ...
- Linux 内核中的 Device Mapper 机制
本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机 ...
- 如何放出Linux内核中的链表大招
前言 上回,我们说到Linux内核中max()宏的终极奥义,Linux内核链表也不甘示弱,那么接下来,让我们看看Linux内核中的链表大招. 如何放出Linux内核中的链表大招 前言 一.链表简介 ( ...
最新文章
- 【Java Web开发指南】线程安全和单线程
- 常见的IE浏览器的一些兼容问题及解决方法
- mysql 优化方法_Mysql的优化方法介绍
- mootools系列:打造属于你自己的Popup(弹出框)——扩展功能篇
- ICPC China Nanchang National Invitational - I. Max answer(线段树+ST)
- 门户网站的免费写手我们可怜的SEOer
- selenium使用浏览器隐私模式加载网站
- mysql表设计 列命名_MYSQL数据库字段命名及设计规范
- html未找到音频文件夹,【已解决】html5中MediaRecorder的dataavailable没有执行获取不到录音数据...
- Html的页面演变史01
- ASP.NET 利用Outlook发送邮件
- 为什么叫区块链存储?兼谈IPFSFilecoin, Chia
- 使用三丰云主机搭建青龙面板保姆级教程
- my firefox常用的插件介绍
- 中国家庭的七大饮食问题
- ZK框架的分析与应用
- python学习之去除字符串中的空格(6种方法)
- Java中的多态概念与实际意义
- The Closest M Points
- Refused to display 'XXX' in a frame because it set 'X-Frame-Options' to 'sameorigin'.