내가 스레드를 별로 좋아하지 않는데, 별 수 없이 써야할 경우가 종종 있다. 그와 별개로 OpenSSL 쓰기를 좋아하는데, OpenSSL을 멀티 스레딩 환경에서 쓰면 자칫 알 수 없는 이유로 죽곤한다. 이유는 OpenSSL 각종 알고리즘엔진이 멀티 스레딩 환경을 고려하지 않은 엔진이라, 경합이 발생하여 충돌이 발생하기 때문이다.
참조: https://www.openssl.org/docs/crypto/threads.html
멀티 스레딩 지원은 0.9.5b-dev부터 지원하였으니, 이하 버전은 포기하자. (응?)
(사실 그 전에도 약간은 지원했으나, CentOS5 기준 0.9.8이니, 이전 버전은 포기하는게 정답이다)
맨페이지를 보면 "crypto/threads/mttest.c 파일에 예제가 있어요 뿌잉뿌잉~"이라는데 나중에 찾아보기 귀찮으니 블로그에 옮겨 놓...으려고 봤는데, 주석도 길고 모든 OS에 대한 전처리기도 있고, C++11도 나왔는데 구닥다리 코드를 쓸 생각이 없으니 내 맘대로 다시 구성했다.
별다른 건 없고, std::mutex를 CRYPTO_num_locks() 호출해서 나온 값만큼 만들고, CRYPTO_set_locking_callback()으로 콜백함수를 세팅한다. 콜백함수에서는 주어진 락 인덱스(type)와 잠금해제여부(mode)로 해당 락을 잠그거나 풀어준다.
CRYPTO_num_locks()은 CRYPTO_NUM_LOCKS int형 상수를 반환하는 것 말고는 하지 않는다. 만약 상수가 필요하면 CRYPTO_NUM_LOCKS를 써도 되겠지만, OpenSSL 버전에 따라 상수를 변경할 수 있으므로 가능하면 함수호출과 동적할당으로 처리하자.
살짝 삼천포로 빠지자면, CRYPTO_NUM_LOCKS는 OpenSSL-1.0.1j 기준으로 41이며, 락을 쓰는 곳은 openssl/crypto/lock.c 소스에서 확인할 수 있다. 해당 소스를 까보면 lock_names라는 static 전역변수가 있는데, 이름 그대로 락 이름을 정의해놓았다. CRYPTO_get_lock_name(int type)으로 이름을 받아올 수 있다.
본론으로 돌아와 락을 걸고 푸는 콜백함수는 "void func (int mode, int type, const char* file, int line)" 형태이다.
int mode는 비트 플래그로 어떤 락을 걸고 풀어야할지 알려준다. RW락을 위해 READ/WRITE 상황까지 전달하지만, 예제도 그렇고 대충 걸고 풀고만 체크한다.
int type은 미리 만들어진 여러개 락 중에 어떤 락을 걸지 알려준다.
file, line은 이 함수를 호출한 소스 위치이며, 디버깅을 위해 사용한다.
main함수에 있는 것처럼 setupLocks()를 호출하여 락 초기화와 콜백함수 등록을 한다. 당연히 어플리케이션 시작하고 스레드 만들기 전에 딱 한 번만 하면 되는 일이다. 총총
* OpenSSL을 사용하는 libcurl(물론 gnutls를 사용할 수도 있지만) 같은 라이브러리에서도 저 작업을 사용자(최종 프로그래머)가 하라고 한다.
* libcurl 참조: http://curl.haxx.se/libcurl/c/threaded-ssl.html
참조: https://www.openssl.org/docs/crypto/threads.html
멀티 스레딩 지원은 0.9.5b-dev부터 지원하였으니, 이하 버전은 포기하자. (응?)
(사실 그 전에도 약간은 지원했으나, CentOS5 기준 0.9.8이니, 이전 버전은 포기하는게 정답이다)
맨페이지를 보면 "crypto/threads/mttest.c 파일에 예제가 있어요 뿌잉뿌잉~"이라는데 나중에 찾아보기 귀찮으니 블로그에 옮겨 놓...으려고 봤는데, 주석도 길고 모든 OS에 대한 전처리기도 있고, C++11도 나왔는데 구닥다리 코드를 쓸 생각이 없으니 내 맘대로 다시 구성했다.
#include <openssl/crypto.h>
#define OPENSSL_THREAD_DEFINES
#include <openssl/opensslconf.h>
#if !defined(OPENSSL_THREADS)
# error "OpenSSL version is not supported multi-thread"
#endif
// C++11 mutex support
#include <mutex>
// Global locks for OpenSSL
static std::mutex* g_locks(nullptr);
// Locking callback function for OpenSSL
static
void
funcLock(int mode, int type, char* file, int line)
{
if ( mode bitand CRYPTO_LOCK ) g_locks[type].lock();
else g_locks[type].unlock();
}
static
unsigned long
funcThreadId(void)
{
return static_cast<unsigned long>(std::hash<std::thread::id>()(std::this_thread::get_id()));
}
// Setup function for OpenSSL MT
bool
setupLocks(void)
{
if ( not g_locks )
{
// Create locks
if ( nullptr == ( g_locks = new std::mutex[CRYPTO_num_locks()] ) ) return false;
// Set callback function for get thread id as unsigned long
CRYPTO_set_id_callback(funcThreadId);
// Set callback function for locking
CRYPTO_set_locking_callback(funcLock);
}
return true;
}
// Main function for example
int
main(int argc, char* argv[])
{
setupLocks();
//blar blar blar...
}
별다른 건 없고, std::mutex를 CRYPTO_num_locks() 호출해서 나온 값만큼 만들고, CRYPTO_set_locking_callback()으로 콜백함수를 세팅한다. 콜백함수에서는 주어진 락 인덱스(type)와 잠금해제여부(mode)로 해당 락을 잠그거나 풀어준다.
CRYPTO_num_locks()은 CRYPTO_NUM_LOCKS int형 상수를 반환하는 것 말고는 하지 않는다. 만약 상수가 필요하면 CRYPTO_NUM_LOCKS를 써도 되겠지만, OpenSSL 버전에 따라 상수를 변경할 수 있으므로 가능하면 함수호출과 동적할당으로 처리하자.
살짝 삼천포로 빠지자면, CRYPTO_NUM_LOCKS는 OpenSSL-1.0.1j 기준으로 41이며, 락을 쓰는 곳은 openssl/crypto/lock.c 소스에서 확인할 수 있다. 해당 소스를 까보면 lock_names라는 static 전역변수가 있는데, 이름 그대로 락 이름을 정의해놓았다. CRYPTO_get_lock_name(int type)으로 이름을 받아올 수 있다.
본론으로 돌아와 락을 걸고 푸는 콜백함수는 "void func (int mode, int type, const char* file, int line)" 형태이다.
int mode는 비트 플래그로 어떤 락을 걸고 풀어야할지 알려준다. RW락을 위해 READ/WRITE 상황까지 전달하지만, 예제도 그렇고 대충 걸고 풀고만 체크한다.
| CRYPTO_LOCK | 0x01 |
| CRYPTO_UNLOCK | 0x02 |
| CRYPTO_READ | 0x04 |
| CRYPTO_WRITE | 0x08 |
int type은 미리 만들어진 여러개 락 중에 어떤 락을 걸지 알려준다.
file, line은 이 함수를 호출한 소스 위치이며, 디버깅을 위해 사용한다.
main함수에 있는 것처럼 setupLocks()를 호출하여 락 초기화와 콜백함수 등록을 한다. 당연히 어플리케이션 시작하고 스레드 만들기 전에 딱 한 번만 하면 되는 일이다. 총총
* OpenSSL을 사용하는 libcurl(물론 gnutls를 사용할 수도 있지만) 같은 라이브러리에서도 저 작업을 사용자(최종 프로그래머)가 하라고 한다.
* libcurl 참조: http://curl.haxx.se/libcurl/c/threaded-ssl.html
댓글
댓글 쓰기