这里我回答我自己的问题。我想这一直发生在所以。
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