盡管ISO C99使用了非常簡單的並且具備移植性的樣例描述了rand函數和srand函數的實現。但是在具體的C語言函數庫的實現上,由於考慮到運行效率以及線程安全,代碼就稍微多了一些。
這里以glibc 2.18為例。
在stdlib目錄下,我們找到rand.c,內容如下:
1 /* Return a random integer between 0 and RAND_MAX. */ 2 int 3 rand (void) 4 { 5 return (int) __random (); 6 }
在同目錄下的random.c,我們找到__random函數,內容如下:
1 long int 2 __random (void) 3 { 4 int32_t retval; 5 6 __libc_lock_lock (lock); 7 8 (void) __random_r (&unsafe_state, &retval); 9 10 __libc_lock_unlock (lock); 11 12 return retval; 13 }
這個函數調用__random_r函數,傳入兩個參數&unsafe_state和&retval,返回retval。所以我們可以確定retval是即將生成的偽隨機數,而unsafe_state是什么呢?
在random.c中,我們找到unsafe_state的定義:
1 static struct random_data unsafe_state = 2 { 3 .fptr = &randtbl[SEP_3 + 1], 4 .rptr = &randtbl[1], 5 6 .state = &randtbl[1], 7 8 .rand_type = TYPE_3, 9 .rand_deg = DEG_3, 10 .rand_sep = SEP_3, 11 12 .end_ptr = &randtbl[sizeof (randtbl) / sizeof (randtbl[0])] 13 };
unsafe_state是一個全局的靜態變量,類型為random_data的結構體。我們查看stdlib.h,找到struct random_data的定義,發現它其實就是我們一直所說的偽隨機數發生器的“種子”,在glibc的實現中,由於在多線程的情況下,如果函數使用一個靜態變量,則這個函數不具備有“可重入”性,就是在多線程調用的情況下會發生意想不到的情形。所以glibc對這種情況作出了修正,保證了rand函數的“可重入性”。首先我們來看random_data的定義:
1 /* Reentrant versions of the `random' family of functions. 2 These functions all use the following data structure to contain 3 state, rather than global state variables. */ 4 5 struct random_data 6 { 7 int32_t *fptr; /* Front pointer. */ 8 int32_t *rptr; /* Rear pointer. */ 9 int32_t *state; /* Array of state values. */ 10 int rand_type; /* Type of random number generator. */ 11 int rand_deg; /* Degree of random number generator. */ 12 int rand_sep; /* Distance between front and rear. */ 13 int32_t *end_ptr; /* Pointer behind state table. */ 14 };
那么glibc是如何保證函數的可重入性的呢?其實就是__random函數中的兩行代碼__libc_lock_lock (lock)和__libc_lock_unlock (lock),這個lock保證了線程在訪問&unsafe_state資源的互斥性,從而保證了函數的可重入性。那么這個lock的機制是從何而來的呢?在random.c文件中我們可以讀到lock的初始化語句:
1 /* POSIX.1c requires that there is mutual exclusion for the `rand' and 2 `srand' functions to prevent concurrent calls from modifying common 3 data. */ 4 __libc_lock_define_initialized (static, lock)
初始化鎖(__libc_lock_define_initialized)、加鎖(__libc_lock_lock)、解鎖(__libc_lock_unlock)的操作屬於宏,我們可以在bits目錄下的libc-lock.h中找到宏的定義(這里說明一下,在我下載的glic源碼中的該文件是stub version,缺少具體的定義,僅有宏名稱。我在unbuntu12.04上找到了相應的bits目錄下的libc-lock.h,屬於NPTL version,有宏的定義):
1 typedef pthread_mutex_t __libc_lock_t; 2 3 # define __libc_lock_define_initialized(CLASS,NAME) \ 4 CLASS __libc_lock_t NAME; 5 6 # define __libc_lock_lock(NAME) \ 7 ({ lll_lock (NAME, LLL_PRIVATE); 0; }) 8 9 # define __libc_lock_unlock(NAME) \ 10 lll_unlock (NAME, LLL_PRIVATE)
而lll_lock和lll_unlock屬於底層的對互斥鎖進行操作的宏,這里不深究。
在保證了函數的“可重入性”之后,rand函數調用鏈條上的最后一環就是__random_r這個函數(在random_r.c中),它真正進行對unsafe_state和retval的操作,產生一個偽隨機數,並且對“種子”進行更新。
1 int 2 __random_r (buf, result) 3 struct random_data *buf; 4 int32_t *result; 5 { 6 int32_t *state; 7 8 if (buf == NULL || result == NULL) 9 goto fail; 10 11 state = buf->state; 12 13 if (buf->rand_type == TYPE_0) 14 { 15 int32_t val = state[0]; 16 val = ((state[0] * 1103515245) + 12345) & 0x7fffffff; 17 state[0] = val; 18 *result = val; 19 } 20 else 21 { 22 int32_t *fptr = buf->fptr; 23 int32_t *rptr = buf->rptr; 24 int32_t *end_ptr = buf->end_ptr; 25 int32_t val; 26 27 val = *fptr += *rptr; 28 /* Chucking least random bit. */ 29 *result = (val >> 1) & 0x7fffffff; 30 ++fptr; 31 if (fptr >= end_ptr) 32 { 33 fptr = state; 34 ++rptr; 35 } 36 else 37 { 38 ++rptr; 39 if (rptr >= end_ptr) 40 rptr = state; 41 } 42 buf->fptr = fptr; 43 buf->rptr = rptr; 44 } 45 return 0; 46 47 fail: 48 __set_errno (EINVAL); 49 return -1; 50 }
在看完了rand函數之后,讓我們來看看srand函數。在目錄中,我們找不到srand.c這樣的文件,但是在random.c中,我們可以看到:
1 weak_alias (__srandom, srandom)
1 weak_alias (__srandom, srand)
這兩行代碼的意思就是為__srandom這個符號設置一個弱符號的別名。什么是弱符號,這里不深究。大致的意思就是如果你在其他的文件中定義了srand函數和srandom函數,你可以放心使用你定義的函數,而不必擔心被這里的弱符號別名所影響。weak_alias的定義在libc-symbols.h當中:
1 /* Define ALIASNAME as a weak alias for NAME. 2 If weak aliases are not available, this defines a strong alias. */ 3 # define weak_alias(name, aliasname) _weak_alias (name, aliasname) 4 # define _weak_alias(name, aliasname) \ 5 extern __typeof (name) aliasname __attribute__ ((weak, alias (#name)));
所以要看srand函數,就看__srandom函數,這個函數接收一個x,在編程當中我們調用srand((unsigned int)time(NULL)),所以x就是當前的時間距離1970年1月1日0時的秒數。函數如下:
1 void 2 __srandom (x) 3 unsigned int x; 4 { 5 __libc_lock_lock (lock); 6 (void) __srandom_r (x, &unsafe_state); 7 __libc_lock_unlock (lock); 8 }
我們在random_r.c中找到__srandom_r函數,這個函數根據傳入的x來改變全局靜態變量unsafe_state的狀態,就是改變了“種子”,所以能夠使偽隨機數發生器根據這個“種子”來產生偽隨機數序列。函數的實現如下:
1 int 2 __srandom_r (seed, buf) 3 unsigned int seed; 4 struct random_data *buf; 5 { 6 int type; 7 int32_t *state; 8 long int i; 9 int32_t word; 10 int32_t *dst; 11 int kc; 12 13 if (buf == NULL) 14 goto fail; 15 type = buf->rand_type; 16 if ((unsigned int) type >= MAX_TYPES) 17 goto fail; 18 19 state = buf->state; 20 /* We must make sure the seed is not 0. Take arbitrarily 1 in this case. */ 21 if (seed == 0) 22 seed = 1; 23 state[0] = seed; 24 if (type == TYPE_0) 25 goto done; 26 27 dst = state; 28 word = seed; 29 kc = buf->rand_deg; 30 for (i = 1; i < kc; ++i) 31 { 32 /* This does: 33 state[i] = (16807 * state[i - 1]) % 2147483647; 34 but avoids overflowing 31 bits. */ 35 long int hi = word / 127773; 36 long int lo = word % 127773; 37 word = 16807 * lo - 2836 * hi; 38 if (word < 0) 39 word += 2147483647; 40 *++dst = word; 41 } 42 43 buf->fptr = &state[buf->rand_sep]; 44 buf->rptr = &state[0]; 45 kc *= 10; 46 while (--kc >= 0) 47 { 48 int32_t discard; 49 (void) __random_r (buf, &discard); 50 } 51 52 done: 53 return 0; 54 55 fail: 56 return -1; 57 }
至此,我們應該可以說自己對glibc中rand函數和srand函數的實現有了初步的認識。
