尽管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 }