내가 스레드를 별로 좋아하지 않는데, 별 수 없이 써야할 경우가 종종 있다. 그와 별개로 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
댓글
댓글 쓰기