OpenSSL can safely be used in multi-threaded applications provided that at least two callback functions are set, locking_function and threadid_func.
OpenSSL 是线程安全的,前提是必须注册两个回调函数。其中根据 Openssl 的版本不同,会有不同 版本的 threadid 回调函数。
如果不注册回调函数,多线程下压力一大必挂无疑。基本都是core dump:
libcrypto.so
下面贴主要代码,如何注册使用这两个回调函数:
.h
1 2 3 4 5 6 7 8 9 10 |
//声明 #ifndef OPENSSL_VERSION_1_0_0 #define OPENSSL_VERSION_1_0_0 0x10000000L #endif #if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_0_0 void ssl_threadid_function_callback(CRYPTO_THREADID* id); #else unsigned long ssl_threadid_function_callback_deprecated(); #endif void ssl_lock_callback(int mode, int type, char *file, int line); |
.c
我这里用的是libuv,所以代码里是uv_mutex_t*,如果是pthreads,就换成 pthread_mutex_t*;相应的 uv_thread_self() 换成 pthread_self()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
//存lock的 static uv_mutex_t* ssl_locks; //初始化 ssl_locks = calloc(CRYPTO_num_locks(), sizeof(uv_mutex_t)); for (int i=0; i< CRYPTO_num_locks(); i++) { uv_mutex_init(&(ssl_locks[i])); } #if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_0_0 CRYPTO_THREADID_set_callback(ssl_threadid_function_callback); #else CRYPTO_set_id_callback(ssl_threadid_function_callback_deprecated); #endif CRYPTO_set_locking_callback(ssl_lock_callback); //销毁 #if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_0_0 CRYPTO_THREADID_set_callback(NULL); #else CRYPTO_set_id_callback(NULL); #endif CRYPTO_set_locking_callback(NULL); for (int i=0; i < CRYPTO_num_locks(); i++) { uv_mutex_destroy(&(ssl_locks[i])); } free(ssl_locks); //回调函数 #if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_0_0 void ssl_threadid_function_callback(CRYPTO_THREADID* id) { CRYPTO_THREADID_set_numeric(id, (unsigned long)uv_thread_self()); } #else unsigned long ssl_threadid_function_callback_deprecated() { return (unsigned long)uv_thread_self(); } #endif void ssl_lock_callback(int mode, int type, char *file, int line) { if (mode & CRYPTO_LOCK) { uv_mutex_lock(&(ssl_locks[type])); LOGT("ssl locked: [type] %d, [file] %s, [line] %d", type, file, line); } else { uv_mutex_unlock(&(ssl_locks[type])); LOGT("ssl unlock: [type] %d, [file] %s, [line] %d", type, file, line); } } |
打开Trace日志,果然一堆lock的坑啊。。。。
…
… ssl locked: [type] 16, [file] ssl_lib.c, [line] 512
… ssl unlock: [type] 16, [file] ssl_lib.c, [line] 512
… ssl locked: [type] 2, [file] ex_data.c, [line] 304
… ssl unlock: [type] 2, [file] ex_data.c, [line] 325
… ssl locked: [type] 2, [file] ex_data.c, [line] 500
… ssl unlock: [type] 2, [file] ex_data.c, [line] 511
…
简单看了下OpenSSL的基本算法源码,可能OpenSSL当年实现的时候内存比较精贵,所以很多算法都是static的内存块,不停new free内存,所以多线程下必须用各种lock。
内存如此便宜的现如今,这种实现方法显然不符合多线程、高并发的潮流了。。。
参考:
http://www.openssl.org/docs/crypto/threads.html
http://blog.csdn.net/yasi_xi/article/details/19125103