公司某服务器的远古程序崩溃了, 分析crash dump发现是因为没解决openssl的线程安全问题,写个小demo记录一下解决方案。
openssl_multithreading_safe_windows_demo: Windows平台下演示如何解决openssl库多线程场景下线程安全问题的64位demo
openssl_lock.h
#ifndef __OPENSSL_LOCK_H__
#define __OPENSSL_LOCK_H__
//
// Windows平台下演示如何解决openssl库多线程场景下线程安全问题的64位demo
// 1.本项目为64位demo项目,32位同理
// 2.openssl存在版本差异,1_0_0前后的加解锁机制不同
// 3.需要根据系统平台差异定义OpensslProc_GetThreadId,如Windows平台下使用GetCurrentThreadId()
// 4.根据实际业务需求定义OpensslProc_LockingHandler,权衡安全性和性能
// 5.OpensslProc_InitLock、OpensslProc_CleanUpLock对应调用
//
#include <iostream>
#include <Windows.h>
//#include <pthread.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#pragma comment(lib, "libssl.lib")
#pragma comment(lib, "libcrypto.lib")
//在开始使用openssl库之前调用
void OpensslProc_InitLock(void);
//当不再使用openssl库以后调用
void OpensslProc_CleanUpLock(void);
//获取当前tid的线程回调
static unsigned long _stdcall OpensslProc_GetThreadId(void);
//给openssl内部线程资源加解锁的回调
static void _stdcall OpensslProc_LockingHandler(int mode, int type, const char* file, int line);
#endif // !__OPENSSL_LOCK_H__
openssl_lock.cpp
#include "openssl_lock.h"
using namespace std;
//Openssl全局加密锁的数量
UINT g_uOpensslCrptLockCount = CRYPTO_num_locks();
//存储g_uOpensslCrptLockCount个互斥体句柄的数组
//每个互斥体与一个openssl内置内部资源(具体是什么资源还有待看源码研究)对应
PHANDLE g_hLock_csArray = NULL;
//获取当前tid的线程回调
static unsigned long _stdcall OpensslProc_GetThreadId(void){
return (unsigned long)GetCurrentThreadId();
}
//给openssl内部线程资源加解锁的回调
static void _stdcall OpensslProc_LockingHandler(int mode, int type, const char* file, int line){
if (type > g_uOpensslCrptLockCount || !g_hLock_csArray[type]){
return;
}
if (mode & CRYPTO_LOCK){
WaitForSingleObject(g_hLock_csArray[type], INFINITE);
} else{
ReleaseMutex(g_hLock_csArray[type]);
}
}
//在开始使用openssl库之前调用
void OpensslProc_InitLock(void){
//根据Openssl内部的crypto_lock的数量申请内存
while (!g_hLock_csArray){
g_hLock_csArray = (HANDLE*)OPENSSL_malloc(g_uOpensslCrptLockCount * sizeof(HANDLE));
}
for (int i = 0; i < CRYPTO_num_locks(); i++){
while (!g_hLock_csArray[i]){
g_hLock_csArray[i] = CreateMutex(NULL, FALSE, NULL);
}
}
//register locking callback
CRYPTO_set_locking_callback((void (*)(int, int, const char*, int))OpensslProc_LockingHandler);
//regist tid callback
#if OPENSSL_VERSION_NUMBER >= 0x1000000f
CRYPTO_THREADID_set_callback(OpensslProc_GetThreadId);
#else
CRYPTO_set_id_callback(OpensslProc_GetThreadId);
#endif
}
//在openssl库使用结束后调用
void OpensslProc_CleanUpLock(void)
{
//unregister tid callback
#if OPENSSL_VERSION_NUMBER >= 0x1000000f
CRYPTO_THREADID_set_callback(NULL);
#else
CRYPTO_set_id_callback(NULL);
#endif
if (!g_hLock_csArray){
return;
}
for (int i = 0; i < CRYPTO_num_locks(); i++){
if (g_hLock_csArray[i]){
CloseHandle(g_hLock_csArray[i]);
g_hLock_csArray[i] = NULL;
}
}
OPENSSL_free(g_hLock_csArray);
}
另外感谢这篇博客的详细讲解 多线程环境下使用openssl_yasi_xi的博客-优快云博客_openssl 多线程