【问题标题】:OpenSSL and multi-threadsOpenSSL 和多线程
【发布时间】:2011-03-25 23:11:48
【问题描述】:

我一直reading 要求,如果在多线程应用程序中使用 OpenSSL,您必须向 OpenSSL 注册线程标识函数(以及互斥体创建函数)。

在Linux上,根据OpenSSL提供的例子,线程通常通过注册这样的函数来识别:

static unsigned long id_function(void){
    return (unsigned long)pthread_self();
}

pthread_self() 返回一个 pthread_t,这适用于 Linux,因为 pthread_t 只是 unsigned long 的 typedef。

在 Windows pthreads、FreeBSD 和其他操作系统上,pthread_t 是一个结构体,具有以下结构:

struct {
    void * p;                   /* Pointer to actual object */
    unsigned int x;             /* Extra information - reuse count etc */ 
}

这不能简单地转换为无符号长整数,当我尝试这样做时,它会引发编译错误。我尝试将 void *p 转换为无符号长整数,理论上内存指针应该在线程之间保持一致和唯一,但这只会导致我的程序崩溃很多。

在使用 Windows pthreads 或 FreeBSD 或任何其他类似这样的操作系统时,我可以向 OpenSSL 注册什么作为线程识别功能?

另外,作为一个附加问题:
有谁知道如果 OpenSSL 被编译成 QT 并与 QT 一起使用,是否也需要这样做,如果需要,如何向 OpenSSL 注册 QThreads?令人惊讶的是,我似乎无法在 QT 的文档中找到答案。

【问题讨论】:

    标签: windows multithreading qt pthreads openssl


    【解决方案1】:

    我将把这段代码放在这里。它不是灵丹妙药,因为它不处理 FreeBSD,但在大多数情况下,当您只需要支持 Windows 并说 Debian 时,它会很有帮助。当然,干净的解决方案假定使用最近推出的CRYPTO_THREADID_* 系列。 (给个思路,它有一个CRYPTO_THREADID_cmp回调,可以映射到pthread_equal

    #include <pthread.h>
    #include <openssl/err.h>
    
    #if defined(WIN32)
        #define MUTEX_TYPE            HANDLE
        #define MUTEX_SETUP(x)        (x) = CreateMutex(NULL, FALSE, NULL)
        #define MUTEX_CLEANUP(x)      CloseHandle(x)
        #define MUTEX_LOCK(x)         WaitForSingleObject((x), INFINITE)
        #define MUTEX_UNLOCK(x)       ReleaseMutex(x)
        #define THREAD_ID             GetCurrentThreadId()
    #else
        #define MUTEX_TYPE            pthread_mutex_t
        #define MUTEX_SETUP(x)        pthread_mutex_init(&(x), NULL)
        #define MUTEX_CLEANUP(x)      pthread_mutex_destroy(&(x))
        #define MUTEX_LOCK(x)         pthread_mutex_lock(&(x))
        #define MUTEX_UNLOCK(x)       pthread_mutex_unlock(&(x))
        #define THREAD_ID             pthread_self()
    #endif
    
    /* This array will store all of the mutexes available to OpenSSL. */ 
    static MUTEX_TYPE *mutex_buf=NULL;
    
    static void locking_function(int mode, int n, const char * file, int line)
    {
        if (mode & CRYPTO_LOCK)
            MUTEX_LOCK(mutex_buf[n]);
        else
            MUTEX_UNLOCK(mutex_buf[n]);
    }
    
    static unsigned long id_function(void)
    {
        return ((unsigned long)THREAD_ID);
    }
    
    int thread_setup(void)
    {
        int i;
    
        mutex_buf = malloc(CRYPTO_num_locks() * sizeof(MUTEX_TYPE));
        if (!mutex_buf)
            return 0;
        for (i = 0;  i < CRYPTO_num_locks(  );  i++)
            MUTEX_SETUP(mutex_buf[i]);
        CRYPTO_set_id_callback(id_function);
        CRYPTO_set_locking_callback(locking_function);
        return 1;
    }
    
    int thread_cleanup(void)
    {
        int i;
        if (!mutex_buf)
            return 0;
        CRYPTO_set_id_callback(NULL);
        CRYPTO_set_locking_callback(NULL);
        for (i = 0;  i < CRYPTO_num_locks(  );  i++)
            MUTEX_CLEANUP(mutex_buf[i]);
        free(mutex_buf);
        mutex_buf = NULL;
        return 1;
    }
    

    【讨论】:

    • 现在我正在努力在我的 OpenSSL 应用程序中使用多个线程。我想更多地了解 set_id 回调:在 openSSL 文档中写道,如果我们不实现此功能,则使用默认实现 - 你知道默认的 imn 窗口是什么吗?它与您的实施不同吗?你知道动态锁回调的一些事情吗——我们也必须实现它吗?谢谢!
    • @RRR 我支持你所有的问题。动态锁在 API 中似乎很新,互联网上大多数使用 OpenSSL 的线程示例都已过时。看起来它应该仍然可以实现向后兼容性,但 dynlocks 可能会增加更好的性能 - 这是我的预感。
    【解决方案2】:

    来自您链接的 OpenSSL 文档:

    需要threadid_func(CRYPTO_THREADID *id)将当前执行线程的标识符记录到id中。此回调的实现不应直接填写 id,但如果线程 ID 是数字,则应使用 CRYPTO_THREADID_set_numeric(),如果是基于指针的,则应使用 CRYPTO_THREADID_set_pointer()。如果应用程序没有使用CRYPTO_THREADID_set_callback() 注册这样的回调,则使用默认实现——在 Windows 和 BeOS 上,这使用系统的默认线程识别 API,而在所有其他平台上,它使用 errno 的地址。当且仅当平台具有线程本地错误编号工具时,后者对于线程安全是令人满意的。

    如图所示,提供您自己的 ID 只有在您可以提供比 OpenSSL 的默认实现更好的 ID 时才真正有用。

    当您不知道 pthread_t 是指针还是整数时,提供 ID 的唯一安全方法是维护您自己的每个线程 ID,并将其存储为线程本地值。

    【讨论】:

      【解决方案3】:

      我只能回答 Qt 部分。使用QThread::currentThreadId(),甚至QThread::currentThread(),因为指针值应该是唯一的。

      【讨论】:

      • 根据文档中的警告,我认为 currentThreadId() 不会起作用。对于currentThread,您是否建议将内存指针转换为long?另外,我认为这意味着即使在 QT 应用程序中你也必须做所有这些事情?
      • 是的,我建议这样做。这很丑陋,但我没有看到任何其他方式。
      猜你喜欢
      • 2015-05-03
      • 1970-01-01
      • 2014-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-11
      • 2016-07-22
      • 1970-01-01
      相关资源
      最近更新 更多