windows OpenSSL 和多线程

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3417706/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 14:59:06  来源:igfitidea点击:

OpenSSL and multi-threads

windowsmultithreadingqtpthreadsopenssl

提问by Nantucket

I've been readingabout the requirement that if OpenSSL is used in a multi-threaded application, you have to register a thread identification function (and also a mutex creation function) with OpenSSL.

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

On Linux, according to the example provided by OpenSSL, a thread is normally identified by registering a function like this:

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

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

pthread_self() returns a pthread_t, and this works on Linux since pthread_t is just a typedef of unsigned long.

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

On Windows pthreads, FreeBSD, and other operating systems, pthread_t is a struct, with the following structure:

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

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

This can't be simply cast to an unsigned long, and when I try to do so, it throws a compile error. I tried taking the void *p and casting that to an unsigned long, on the theory that the memory pointer should be consistent and unique across threads, but this just causes my program to crash a lot.

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

What can I register with OpenSSL as the thread identification function when using Windows pthreads or FreeBSD or any of the other operating systems like this?

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

Also, as an additional question:
Does anyone know if this also needs to be done if OpenSSL is compiled into and used with QT, and if so how to register QThreads with OpenSSL?Surprisingly, I can't seem to find the answer in QT's documentation.

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

回答by Tosha

I will just put this code here. It is not panacea, as it doesn't deal with FreeBSD, but it is helpful in most cases when all you need is to support Windows and and say Debian. Of course, the clean solution assumes usage of CRYPTO_THREADID_*family introduced recently. (to give an idea, it has a CRYPTO_THREADID_cmpcallback, which can be mapped to pthread_equal)

我只会把这段代码放在这里。它不是灵丹妙药,因为它不处理 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;
}

回答by Dr. Snoopy

I only can answer the Qt part. Use QThread::currentThreadId(), or even QThread::currentThread()as the pointer value should be unique.

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

回答by jowo

From the OpenSSL doc you linked:

从您链接的 OpenSSL 文档中:

threadid_func(CRYPTO_THREADID *id)is needed to record the currently-executing thread's identifier into id. The implementation of this callback should not fill in id directly, but should use CRYPTO_THREADID_set_numeric()if thread IDs are numeric, or CRYPTO_THREADID_set_pointer()if they are pointer-based. If the application does not register such a callback using CRYPTO_THREADID_set_callback(), then a default implementation is used - on Windows and BeOS this uses the system's default thread identifying APIs, and on all other platforms it uses the address of errno. The latter is satisfactory for thread-safety if and only if the platform has a thread-local error number facility.

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

As shown providing your own ID is really only useful if you can provide a better ID than OpenSSL's default implementation.

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

The only fail-safe way to provide IDs, when you don't know whether pthread_tis a pointer or an integer, is to maintain your own per-thread IDs stored as a thread-local value.

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