从Linux内核中获取真随机数【转】

转自:http://blog.csdn.net/adamska0104/article/details/45743087

 内核随机数产生器

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。

时间: 2024-09-30 18:49:22

从Linux内核中获取真随机数【转】的相关文章

Linux内核中双向链表的经典实现

概要 前面一章"介绍双向链表并给出了C/C++/Java三种实现",本章继续对双向链表进行探讨,介绍的内容是Linux内核中双向链表的经典实现和用法.其中,也会涉及到Linux内核中非常常用的两个经典宏定义offsetof和container_of.内容包括: 1. Linux中的两个经典宏定义 2. Linux中双向链表的经典实现 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3562146.html 更多内容: 数据结构与算法系列 目录

Linux内核中的内存管理浅谈

 [十月往昔]--Linux内核中的内存管理浅谈 为什么要叫做"十月往昔"呢?是为了纪念我的原博客. 不知道为什么,突然想来一个新的开始--而那个博客存活至今刚好十个月,也有十个月里的文档. 十月往昔,总有一些觉得珍贵的,所以搬迁到这里来. 而这篇文章是在09.04.20-09.04.21里写的. Jason Lee   ------------–cut-line   1.基本框架(此处主要谈页式内存管理) 4G是一个比较敏感的字眼,早些日子,大多数机器(或者说操作系统)支持的内存上限

详解Linux内核中的container_of函数_unix linux

前言 在linux 内核中,container_of 函数使用非常广,例如 linux内核链表 list_head.工作队列work_struct中. 在linux内核中大名鼎鼎的宏container_of() ,其实它的语法很简单,只是一些指针的灵活应用,它分两步:       第一步,首先定义一个临时的数据类型(通过typeof( ((type *)0)->member )获得)与ptr相同的指针变量__mptr,然后用它来保存ptr的值.       第二步,用(char *)__mptr

Linux内核中的list.h浅谈

[十月往昔]--Linux内核中的list.h浅谈 为什么要叫做"十月往昔"呢,是为了纪念我的原博客. 不知道为什么,突然想来一个新的开始--而那个博客存活至今刚好十个月,也有十个月里的文档. 十月往昔,总有一些觉得珍贵的,所以搬迁到这里来. 而这篇文章是在09.04.10里写的. Jason Lee   ------------–cut-line /*------------------------------- include/linux/list.h -2.6.29 */ 该文件

Linux内核中链表的实现与应用【转】

转自:http://blog.chinaunix.net/uid-27037833-id-3237153.html 链表(循环双向链表)是Linux内核中最简单.最常用的一种数据结构.                1.链表的定义             struct list_head {                 struct list_head *next, *prev;             }            这个不含数据域的链表,可以嵌入到任何数据结构中,例如可按如下方

浅析linux内核中timer定时器的生成和sofirq软中断调用流程【转】

转自:http://blog.chinaunix.net/uid-20564848-id-73480.html 浅析linux内核中timer定时器的生成和sofirq软中断调用流程   mod_timer添加的定时器timer在内核的软中断中发生调用,__run_timers会spin_lock_irq(&base->lock);禁止cpu中断,所以我们的timer回调处理函数handler工作在irq关闭的环境中,所以需要作很多考虑,比如在handler中尽量不要执行会引起pending

Linux内核中常见内存分配函数【转】

 转自:http://blog.csdn.net/wzhwho/article/details/4996510 1.      原理说明 Linux内核中采用了一种同时适用于32位和64位系统的内存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系统中,用到了四级页表,如图2-1所示.四级页表分别为: l         页全局目录(Page Global Directory) l         页上级目录(Page Upper Directory) l         页中间

Linux内核中的jiffies及其作用介绍及jiffies等相关函数详解

在LINUX的时钟中断中涉及至二个全局变量一个是xtime,它是timeval数据结构变量,另一个则是jiffies,首先看timeval结构struct timeval{time_t tv_sec; /***second***/susecond_t tv_usec;/***microsecond***/}到底microsecond是毫秒还是微秒?? 1秒=1000毫秒(3个零),1秒=1000 000微秒(6个零),1秒=1000 000 000纳秒(9个零),1秒=1000 000 000

大话Linux内核中锁机制之信号量、读写信号量

大话Linux内核中锁机制之信号量.读写信号量 在上一篇博文中笔者分析了关于内存屏障.读写自旋锁以及顺序锁的相关内容,本篇博文将着重讨论有关信号量.读写信号量的内容.  六.信号量 关于信号量的内容,实际上它是与自旋锁类似的概念,只有得到信号量的进程才能执行临界区的代码:不同的是获取不到信号量时,进程不会原地打转而是进入休眠等待状态.它的定义是include\linux\semaphore.h文件中,结构体如图6.1所示.其中的count变量是计数作用,通过使用lock变量实现对count变量的