我的应用程序(多线程守护进程)广泛使用OpenSSL函数,包括SSL_Read,SSL_Write,SSL证书的生成和验证等。 当前已链接到OpenSSL 1.0.2xx 该应用程序提供网络服务,侦听接受连接,还创建与远程对等方的连接。
我遇到的一个典型问题是内存利用率的不断提高。 谷歌搜索,我意识到基本的需要在线程退出之前调用ERR_remove_thread_state(NULL)。 我还意识到需要间歇性地调用ERR_clear_error()。
但是,每次应用程序创建超过1024个并发线程时,我仍然会注意到出现峰值。
以下代码是我到目前为止所学内容的实现。 SSL_thread_setup()基本上是OpenSSL Wiki中的示例的改编版,我只是通过一次调用来调用所有相关事物的初始化。 SSL_pthreads_locking_callback()按照OpenSSL软件包中的示例进行。 SSL_Mem_clean()是一种与ssl中间错误队列相关的内存刷新程序。 SSL_Threads_clean()用于在线程退出之前正确清理它,以防止任何内存泄漏。
#if 0
/*
Actual code to handle initialization of OpenSSL, in a multi-threaded daemon application
*/
#endif
#ifdef __FD_SETSIZE
#undef __FD_SETSIZE
#endif
#ifdef IS_64BIT
#define __FD_SETSIZE 65472
#else
#define __FD_SETSIZE 8192
#endif
#ifndef FD_SETSIZE
#define FD_SETSIZE __FD_SETSIZE
#endif
pthread_mutex_t _ssl_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t *lock_cs;
long *lock_count;
/*
Initialize OpenSSL libraries and setup for multi-threading
*/
void SSL_thread_setup(void)
{
int i;
int x = -1;
char buf[1024] = "";
lock_cs=(pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
lock_count=(long *)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
for (i=0; i<CRYPTO_num_locks(); i++) {
lock_count[i]=0;
pthread_mutex_init(&(lock_cs[i]),NULL);
}
CRYPTO_set_id_callback((unsigned long (*)())SSL_pthreads_thread_id);
CRYPTO_set_locking_callback((void (*)(int, int, const char *, int)) SSL_pthreads_locking_callback);
for (x = 0; x < sizeof(buf); x++)
buf[x] = rand() % 255;
RAND_seed(buf, sizeof(buf));
SSL_load_error_strings ();
SSL_library_init ();
OpenSSL_add_all_algorithms ();
OPENSSL_config (NULL);
}
void SSL_pthreads_locking_callback(int mode, int type, const char *file, int line)
{
if (mode & CRYPTO_LOCK) {
pthread_mutex_lock(&(lock_cs[type]));
lock_count[type]++;
} else
pthread_mutex_unlock(&(lock_cs[type]));
}
unsigned long SSL_pthreads_thread_id(void)
{
return (unsigned long)pthread_self();
}
/*
Intermediate SSL error queue cleaner
*/
void SSL_Mem_clean()
{
ERR_clear_error();
ERR_remove_thread_state(NULL);
// ERR_free_strings(); // crashes
}
/*
Prevent typical "memory leaks" due to multi-threading
*/
void SSL_Threads_clean()
{
#ifndef OPENSSL_NO_ENGINE
// ENGINE_cleanup();
#endif
ERR_free_strings();
ERR_clear_error();
ERR_remove_thread_state(NULL);
pthread_mutex_lock(&_ssl_lock);
FIPS_mode_set(0);// good
// ENGINE_cleanup(); // crashes
// EVP_cleanup(); // This causes crashes
// CRYPTO_cleanup_all_ex_data(); // This causes crashes
pthread_mutex_unlock(&_ssl_lock);
}
这是我的用法:
#if 0
The dummy code below just exemplifies the usage of:
SSL_thread_setup()
SSL_Mem_clean()
SSL_Threads_clean()
#endif
/*
Generic main of a daemon process, that may spawn multiple threads
*/
int main ()
{
/* do any pre-forking activity */
/* setup the signal handlers */
s_signal_setup();
SSL_thread_setup();
/* initialize threads array */
threads_init();
fpid = fork();
switch (fpid) {
case -1:
fprintf(stderr, "first fork: failed\n");
exit(EXIT_FAILURE);
break;
case 0:
chdir("/");
setsid();
umask(0);
fprintf(stdout, "%s:%d fork: \n", __FUNCTION__ , __LINE__ );
fpid = fork();
switch (fpid) {
case -1:
fprintf(stderr, "second fork: failed\n");
exit(EXIT_FAILURE);
break;
case 0:
umask(022);
fix_gid();
fix_uid();
umask(027);
/* call the main purpose of the application */
mainloop();
break;
}
}
return 0;
}
/*
Called when a thread exits to ensure cleaning to prevent any memory leaks
*/
void thread_release ()
{
SSL_Threads_clean();
pthread_exit(0);
}
/*
The actual process of the application
*/
void mainloop ()
{
void *data_structure;
while (1) {
data_structure = get_new_data();
process_new_data(data_structure);
}
}
/*
call to process data in a thread
*/
void process_new_data(void *data_structure)
{
if (!found_unused_idle_thread (data_structure) ) {
create_a_detatched_thread((void *) threaded_worker_function, data_structure);
}
SSL_Mem_clean();
}
/*
a detatched thread, that may be reused, to process data
*/
void threaded_worker_function(void *data_structure)
{
while (1) {
SSL_Mem_clean();
/* process data_structure */
some_process(data_structure);
if (timed_signal_wait_for_new_data)
continue;
thread_release();
}
}
/*
enable caller to pass new data to an unused/idle thread
*/
bool found_unused_idle_thread (data_structure)
{
if (have_unused_thread) {
assign_to_waiting_thread(data_structure);
signal_waiting_thread_to_resume();
SSL_Mem_clean();
return true;
}
return false;
}
/*
The primary function in a thread that may cause invocation of other functions requiring OpenSSL
*/
void some_process(data_structure)
{
/* calls various sub-routines */
sub_routine();
SSL_Mem_clean();
}
/*
a child function called that may use OpenSSL functions
*/
void sub_routine()
{
/* calls further sub-routines,
each sub-routine calls SSL_Mem_clean()
*/
SSL_Mem_clean();
}
我缺少什么吗?还是可以更好?
问题:是否从正确的位置调用SSL_thread_setup()?分叉后应该更应该称呼它吗?
问题:SSL_thread_setup()中的事物顺序正确吗?缺少什么吗?
我敢肯定,这里有一些好的技巧可以发现需要改进的地方。
谢谢。