Мое приложение (многопоточный процесс-демон) широко использует функции OpenSSL, включая SSL_Read, SSL_Write, генерацию и проверку SSL-сертификатов и т. Д.
В настоящее время он связан с OpenSSL 1.0.2xx
Приложение предоставляет сетевой сервис, прослушивает прием подключений, а также создает подключения к удаленным одноранговым узлам.
Одна типичная проблема, с которой я сталкиваюсь, - это постоянное увеличение использования памяти.
Погуглив, я понял, что кардинально нужно вызывать ERR_remove_thread_state (NULL) до выхода из потока.
Я также осознал необходимость периодически вызывать ERR_clear_error ().
Однако я продолжаю замечать появление пика каждый раз, когда приложение создает более 1024 одновременных потоков.
Код ниже - моя реализация того, что я изучил до сих пор.
SSL_thread_setup () - это, по сути, адаптация примеров в вики OpenSSL, я просто призываю инициализировать все важные вещи одним вызовом.
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 ()? Чего не хватает?
Я уверен, что некоторые из хороших хаков могут обнаружить области улучшения.
Заранее спасибо.