從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 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. );
for (i=0; i<NUM; i++)
{
get_random_bytes(&randNum[i], sizeof (unsigned long ));
printk(KERN_ALERT "We get random number: %ld , randNum[i]);
}
return 0;
} static void random_exit( void )
{ printk(KERN_ALERT "quit get_random_num. );
} 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。
相關文章
- Java從List中獲取隨機元素Java隨機
- java獲取時間戳和隨機數Java時間戳隨機
- python爬蟲從ip池獲取隨機IPPython爬蟲隨機
- Java之獲取隨機數的4種方法Java隨機
- 獲取當前時間戳和隨機數的獲取、Java Random、ThreadLocalRandom、UUID類中的方法應用(隨機數)時間戳隨機JavarandomthreadUI
- 【API】隨機獲取圖片API隨機
- JavaScript 獲取0-1之間的隨機數JavaScript隨機
- Django Models隨機獲取指定數量資料方法Django隨機
- Pythonrandom模組(獲取隨機數)常用方法和使用例子Pythonrandom隨機
- Linux Shell 生成隨機數和隨機字串Linux隨機字串
- Android 從手機相簿獲取圖片 uri 路徑 從相機獲取照片Android
- 獲取Linux本機IP命令Linux
- ArcGIS如何自動獲得隨機取樣點?隨機
- layui獲取頁面checkbox核取方塊值UI
- JavaScript隨機數實現防止快取JavaScript隨機快取
- 如何從context-param獲取引數?Context
- linux c 獲取系統程式總數Linux
- JavaScript 獲取選中checkbox核取方塊的值JavaScript
- Linux核心中斷Linux
- 獲取客戶端真實IP客戶端
- 碎片化學習Java(二十七)Java獲取 0 到 9 內的隨機數Java隨機
- JavaScript獲取選中checkbox核取方塊的選中值JavaScript
- 基於隨機定位的地圖資訊獲取方式隨機地圖
- 從已執行容器獲取 docker run 引數Docker
- 在Linux中,如何獲取CPU的總核心數?Linux
- 隨機數隨機
- python生成隨機數、隨機字串Python隨機字串
- .net 獲取客戶端真實ip客戶端
- jquery獲取圖片的真實大小jQuery
- JavaScript獲取圖片的真實大小JavaScript
- Linux 獲取幫助Linux
- Linux 獲取系統開機/啟動時間Linux
- 簡單的Bindservice服務獲取隨機數,需要在清單檔案中註冊service隨機
- 真隨筆
- 真:隨筆
- mssql sqlserver 從指定字串中獲取數字的方法SQLServer字串
- 直播軟體原始碼,JS獲取指定長度的隨機字元原始碼JS隨機字元
- 【Python小隨筆】 SSH 獲取資訊Python