Openssl : thread safety

https://www.openssl.org/docs/man1.0.2/crypto/threads.html

OpenSSL can safely be used in multi-threaded applications provided that at least two callback functions are set, locking_function and threadid_func.

根据官方文档,为了让OpsnSSL是线程安全的,需要至少提供两个 callback function:

  • locking_function
  • threadid_func

locking_function

locking_function(int mode, int n, const char *file, int line) is needed to perform locking on shared data structures. (Note that OpenSSL uses a number of global data structures that will be implicitly shared whenever multiple threads use OpenSSL.) Multi-threaded applications will crash at random if it is not set.

locking_function() must be able to handle up to CRYPTO_num_locks() different mutex locks. It sets the n-th lock if mode & CRYPTO_LOCK, and releases it otherwise.

file and line are the file number of the function setting the lock. They can be useful for debugging.

locking_function(int mode, int n, const char *file, int line)需要对共享的数据结构进行加锁。(OpenSSL使用一些全局的数据结构,因此在多线程环境下会有问题。)如果没有设置此callback function,那么多线程OpenSLL程序会随机意外的crash。(如果没有设置此回调函数,你的程序也没crash,不要认为这么做没有问题,只是crash还没有发生而已)

locking_function必须能够处理最多CRYPTO_num_locks()个不同的mutex锁,如果 mode & CRYPTO_LOCK 为true, 它会lock 第n个锁;否则unlock。

参数flie和line用于debug。

threadid_func

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)需要将当前执行线程的identifier记录到id中。

一个完整的例子

https://github.com/openssl/openssl/blob/OpenSSL_1_0_2g/crypto/threads/th-lock.c

上面是OpenSSL 1.0.2g版本中的代码,已经封装好了两个函数:

void CRYPTO_thread_setup(void);   // 注册回调
void CRYPTO_thread_cleanup(void); // 执行清理工作

关键代码:

static pthread_mutex_t *lock_cs;
static long *lock_count;

void CRYPTO_thread_setup(void)
{
    int i;

    lock_cs = (pthread_mutex_t*)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
    lock_count = (long *)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
    if (!lock_cs || !lock_count) {
        /* Nothing we can do about this...void function! */
        if (lock_cs)
            OPENSSL_free(lock_cs);
        if (lock_count)
            OPENSSL_free(lock_count);
        return;
    }
    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 (*)())pthreads_thread_id);
    CRYPTO_set_locking_callback((void (*)(int, int, const char*, int))pthreads_locking_callback);
}

void thread_cleanup(void)
{
    int i;

    CRYPTO_set_locking_callback(NULL);
    for (i = 0; i < CRYPTO_num_locks(); i++) {
        pthread_mutex_destroy(&(lock_cs[i]));
    }
    OPENSSL_free(lock_cs);
    OPENSSL_free(lock_count);
}

void pthreads_locking_callback(int mode, int type, char *file, int line)
{
# if 0
    fprintf(stderr, "thread=%4d mode=%s lock=%s %s:%d\n",
            CRYPTO_thread_id(),
            (mode & CRYPTO_LOCK) ? "l" : "u",
            (type & CRYPTO_READ) ? "r" : "w", file, line);
# endif
# if 0
    if (CRYPTO_LOCK_SSL_CERT == type)
        fprintf(stderr, "(t,m,f,l) %ld %d %s %d\n",
                CRYPTO_thread_id(), mode, file, line);
# endif
    if (mode & CRYPTO_LOCK) {
        pthread_mutex_lock(&(lock_cs[type]));
        lock_count[type]++;
    } else {
        pthread_mutex_unlock(&(lock_cs[type]));
    }
}

unsigned long pthreads_thread_id(void)
{
    unsigned long ret;

    ret = (unsigned long)pthread_self();
    return (ret);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值