這裏我回答我自己的問題。我想這一直髮生在所以。
OpenSSL中的BIGNUM是一個複雜的結構,它保存了一個任意大的數字,因此反覆創建和釋放BIGNUM實例會導致相當大的開銷。 BIGNUM上下文或BN_CTX被創建並用於保存此開銷。
結構
的BN_CTX結構包含兩種結構:BN_POOL
和BN_STACK
。 BN_POOL
用一個鏈接列表保留一組臨時的bignum,而BN_STACK
管理這個棧幀。
上創建
一個BN_CTX
例如ctx
與BN_CTX_new()
創建。函數必須先調用BN_CTX_start()
才能獲得新的堆棧幀。通過調用BN_CTX_get(ctx)
,OpenSSL在ctx
的BN_POOL
中查找未使用的bignum。如果沒有任何可用的臨時數字,OpenSSL將創建一個並鏈接到鏈接列表。這必須在通過ctx
作爲其他功能的參數之前完成。
當然有一種機制可以防止用戶創建太多的臨時牌。您可以在BN_POOL
中創建的預定義數字的個數爲16.一旦超出限制,可能的分段錯誤將發生在OpenSSL庫中的隨機位置。
在退出
功能後與BIGNUM實例從ctx
了,並準備退出做,BN_CTX_end()
被調用,以釋放臨時大數,這意味着這些大數成爲「未使用」,可以是請求下一個BN_CTX_get()
。
最後,也許以後的BN_CTX_start()
和BN_CTX_end()
幾次,BN_CTX_end()
被稱爲自由BN_STACK
結構,明確免費大數在BN_POOL
。
示例代碼
void foo(){
BN_CTX* ctx;
ctx = BN_CTX_new();
/* Using BIGNUM context in a series of BIGNUM operations */
bar(ctx);
bar(ctx);
bar(ctx);
/* Using BIGNUM context in a function called in loops */
while(/*condition*/){
bar(ctx);
}
BN_CTX_free(ctx);
}
而這裏的功能bar()
void bar(BN_CTX* ctx){
BIGNUM *bn;
BN_CTX_start(ctx);
bn = BN_CTX_get(ctx);
/* Do something with bn */
BN_CTX_end(ctx);
}
功能foo()
創建一個新的BIGNUM上下文並把它作爲參數傳遞給函數bar()
。在第一次bar()
調用BN_CTX_get()
時,會創建一個臨時的bignum並存儲在BN_POOL
中並返回。 BN_CTX_get()
在隨後的bar()
中不會創建新的bignum,而是返回它首先創建的那個。這個臨時性質將在foo()
中被BN_CTX_free()
最終清除。
結論
當性能是關注的,使用BN_CTX
通過將其傳遞到
- 需要BIGNUM結構保存臨時大數字功能,以節省BIGNUM創建的開銷,以及
- 被順序調用來執行某些bignum操作,或者
- 在循環中被重複調用。
請注意,存儲在BN_CTX
中的雙子的數量存在限制。如果性能不是問題,那麼使用
bn = BN_new();
if (bn)
BN_free(bn);
就好。
什麼是堆棧幀? – updogliu 2013-12-30 06:47:56
由於一個BN_CTX對象'ctx'可以從函數傳遞給函數,棧幀是用來追蹤這些函數調用深度的信息(它傳遞'ctx'作爲參數)以及分配給內存的大小'ctx'。 – ChiaraHsieh 2013-12-30 07:06:46
我想這是釋放tmp變量函數的功能。如果有一個函數使用了兩個tmp BIGNUM'bn1 = BN_CTX_get(ctx); bn2 = BN_CTX_get(ctx);'對我來說,釋放'bn1'而不是'bn2'是沒有辦法的。它們只能在BN_CTX_end上一起發佈。棧幀信息是要記住這個函數使用了兩個tmps'bn1'和'bn2',我猜。 – updogliu 2013-12-30 15:48:02