math/rand
math/rand
實現了偽隨機數算法,和其它的編程語言類似,操作邏輯都是先設置隨機數種子,然后再獲取隨機數序列。這樣每次生成的隨機數序列都是不一樣的。
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())
for i := 0; i < 10; i++ {
// 生成0~99的隨機數
fmt.Println(rand.Intn(100))
}
}
並且go還保證了上述的代碼生成隨機數是線程安全的,因為里面的操作加鎖了,但可能有很少的場景強調效率,也可以自己創建一個偽隨機源,這樣就不會加鎖了。
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < 10; i++ {
// 生成0~99的隨機數
fmt.Println(r.Intn(100))
}
}
crypto/rand
上面math/rand
包生成的隨機數是偽隨機的,可以根據上一個隨機數的值計算出下一個,只是在總體的分布上是均勻的,來模擬隨機。
但有些場景需要密碼學安全的隨機數,也就是無法預測的隨機數,所以go語言提供了crypto/rand
包,里面提供了真隨機數產生接口,滿足密碼學安全的需求。
package main
import (
"crypto/rand"
"fmt"
)
func main() {
b := make([]byte, 10)
// Reader is a global, shared instance of a cryptographically
// secure random number generator.
//
// On Linux and FreeBSD, Reader uses getrandom(2) if available, /dev/urandom otherwise.
// On OpenBSD, Reader uses getentropy(2).
// On other Unix-like systems, Reader reads from /dev/urandom.
// On Windows systems, Reader uses the CryptGenRandom API.
// On Wasm, Reader uses the Web Crypto API.
rand.Read(b)
fmt.Println(b)
}
linux真隨機數
為了獲得真正意義上的隨機數,需要一個外部的噪聲源。Linux內核找到了一個完美的噪聲源產生者--就是使用計算機的人。
內核根據非確定性的設備事件維護着一個熵池,池中的數據是完全隨機的。當有新的設備事件到來,內核會估計新加入的數據的隨機性,當我們從熵池中取出數據時,內核會減少熵的估計值。
/dev/random
和/dev/urandom
這兩個特殊設備都是字符型設備。我們可以在用戶空間通過read系統調用讀這兩個設備文件以此獲取隨機數。這兩個設備文件的區別在於:如果內核熵池的估計值為0時,/dev/random
將被阻塞,而/dev/urandom
不會有這個限制。