从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,如需转载请自行联系原作者


相关文章
|
1天前
|
Linux 数据库
Linux内核中的锁机制:保障并发操作的数据一致性####
【10月更文挑战第29天】 在多线程编程中,确保数据一致性和防止竞争条件是至关重要的。本文将深入探讨Linux操作系统中实现的几种关键锁机制,包括自旋锁、互斥锁和读写锁等。通过分析这些锁的设计原理和使用场景,帮助读者理解如何在实际应用中选择合适的锁机制以优化系统性能和稳定性。 ####
14 6
|
2天前
|
机器学习/深度学习 负载均衡 算法
深入探索Linux内核调度机制的优化策略###
本文旨在为读者揭开Linux操作系统中至关重要的一环——CPU调度机制的神秘面纱。通过深入浅出地解析其工作原理,并探讨一系列创新优化策略,本文不仅增强了技术爱好者的理论知识,更为系统管理员和软件开发者提供了实用的性能调优指南,旨在促进系统的高效运行与资源利用最大化。 ###
|
4天前
|
算法 Linux 开发者
深入探究Linux内核中的内存管理机制
本文旨在对Linux操作系统的内存管理机制进行深入分析,探讨其如何通过高效的内存分配和回收策略来优化系统性能。文章将详细介绍Linux内核中内存管理的关键技术点,包括物理内存与虚拟内存的映射、页面置换算法、以及内存碎片的处理方法等。通过对这些技术点的解析,本文旨在为读者提供一个清晰的Linux内存管理框架,帮助理解其在现代计算环境中的重要性和应用。
|
2天前
|
缓存 网络协议 Linux
Linux操作系统内核
Linux操作系统内核 1、进程管理: 进程调度 进程创建与销毁 进程间通信 2、内存管理: 内存分配与回收 虚拟内存管理 缓存管理 3、驱动管理: 设备驱动程序接口 硬件抽象层 中断处理 4、文件和网络管理: 文件系统管理 网络协议栈 网络安全及防火墙管理
19 4
|
4天前
|
人工智能 算法 大数据
Linux内核中的调度算法演变:从O(1)到CFS的优化之旅###
本文深入探讨了Linux操作系统内核中进程调度算法的发展历程,聚焦于O(1)调度器向完全公平调度器(CFS)的转变。不同于传统摘要对研究背景、方法、结果和结论的概述,本文创新性地采用“技术演进时间线”的形式,简明扼要地勾勒出这一转变背后的关键技术里程碑,旨在为读者提供一个清晰的历史脉络,引领其深入了解Linux调度机制的革新之路。 ###
|
6天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
30 4
|
7天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
10天前
|
缓存 Linux
揭秘Linux内核:探索CPU拓扑结构
【10月更文挑战第26天】
27 1
|
10天前
|
缓存 运维 Linux
深入探索Linux内核:CPU拓扑结构探测
【10月更文挑战第18天】在现代计算机系统中,CPU的拓扑结构对性能优化和资源管理至关重要。了解CPU的核心、线程、NUMA节点等信息,可以帮助开发者和系统管理员更好地调优应用程序和系统配置。本文将深入探讨如何在Linux内核中探测CPU拓扑结构,介绍相关工具和方法。
11 0
|
7天前
|
缓存 算法 Linux
Linux内核中的内存管理机制深度剖析####
【10月更文挑战第28天】 本文深入探讨了Linux操作系统的心脏——内核,聚焦其内存管理机制的奥秘。不同于传统摘要的概述方式,本文将以一次虚拟的内存分配请求为引子,逐步揭开Linux如何高效、安全地管理着从微小嵌入式设备到庞大数据中心数以千计程序的内存需求。通过这段旅程,读者将直观感受到Linux内存管理的精妙设计与强大能力,以及它是如何在复杂多变的环境中保持系统稳定与性能优化的。 ####
14 0
下一篇
无影云桌面