2010-08-05 12 views
5

He estado llamando al reading sobre el requisito de que si se usa OpenSSL en una aplicación de subprocesos múltiples, debe registrar una función de identificación de subprocesos (y también una función de creación de mutex) con OpenSSL.OpenSSL y multi-threads

En Linux, de acuerdo con el ejemplo proporcionado por OpenSSL, un hilo normalmente se identifica mediante el registro de una función como esta:

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

pthread_self() devuelve una pthread_t, y esto funciona en Linux desde pthread_t es sólo un typedef de unsigned long.

En pthreads Windows, FreeBSD y otros sistemas operativos, pthread_t es una estructura, con la siguiente estructura:

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

Esto no se puede simplemente arrojado a un largo sin signo, y cuando intento hacer por lo tanto, arroja un error de compilación. Intenté tomar el vacío * p y convertirlo en un tiempo sin signo, con la teoría de que el puntero de memoria debería ser coherente y único en todos los hilos, pero esto solo hace que mi programa se bloquee mucho.

¿Qué puedo registrar con OpenSSL como la función de identificación de subprocesos cuando uso Windows pthreads o FreeBSD o cualquiera de los otros sistemas operativos como este?

También, como una cuestión adicional:
¿alguien sabe si esto también necesita ser hecho si se compila en OpenSSL y utilizar con QT, y si es así cómo registrarse QThreads con OpenSSL? Sorprendentemente, parece que no puedo encontrar la respuesta en la documentación de QT.

+2

Bueno, parece que OpenSSL es un pedazo de @ # $% entonces. Si escribo una aplicación (de un solo subproceso) que utiliza OpenSSL para alguna tarea, y alguien escribe una biblioteca (dll) que usa (incidentalmente) OpenSSL para alguna otra tarea: ¿cómo diablos se supone que estos dos componentes completamente independientes saben de los demás? existencia, hasta el punto de que uno de ellos (a pesar de ser de un solo subproceso) necesita proporcionar esta funcionalidad de subprocesamiento. –

Respuesta

1

Solo puedo responder a la parte Qt. Use QThread::currentThreadId() o incluso QThread::currentThread() ya que el valor del puntero debe ser único.

+0

No creo que currentThreadId() funcione en función de las advertencias en la documentación. Para currentThread, ¿estás sugiriendo lanzar el puntero de memoria en un largo? Además, supongo que esto significa que tienes que hacer todo esto incluso en aplicaciones de QT. – Nantucket

+0

Sí, estoy sugiriendo eso. Es feo, pero no veo otra forma. –

1

Desde el doc OpenSSL se conectó:

threadid_func(CRYPTO_THREADID *id) se necesita para registrar el identificador del hilo actualmente en ejecución de Identificación. La implementación de esta devolución de llamada no debe completar el ID directamente, sino que debe usar CRYPTO_THREADID_set_numeric() si los ID de subprocesos son numéricos, o CRYPTO_THREADID_set_pointer() si están basados ​​en punteros. Si la aplicación no registra dicha devolución de llamada usando CRYPTO_THREADID_set_callback(), entonces se usa una implementación predeterminada: en Windows y BeOS esto utiliza las API de identificación de subprocesos predeterminadas del sistema, y ​​en todas las otras plataformas usa la dirección de errno. Este último es satisfactorio para la seguridad de subprocesos si y solo si la plataforma tiene una facilidad de número de error local de subprocesos.

Como se muestra, proporcionar su propia ID solo es realmente útil si puede proporcionar una identificación mejor que la implementación predeterminada de OpenSSL.

La única forma a prueba de fallas para proporcionar identificadores, cuando no se sabe si pthread_t es un puntero o un entero, es mantener sus propios ID por subproceso almacenados como un valor local de subproceso.

5

Voy a poner este código aquí. No es una panacea, ya que no trata con FreeBSD, pero es útil en la mayoría de los casos cuando todo lo que necesita es dar soporte a Windows y decir Debian. Por supuesto, la solución limpia asume el uso de la familia CRYPTO_THREADID_* presentada recientemente.(Para dar una idea, tiene una devolución de llamada CRYPTO_THREADID_cmp, que puede ser asignada a 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; 
} 
+0

En este momento estoy trabajando para permitir el uso de múltiples hilos con mi aplicación OpenSSL. y me gustaría saber un poco más sobre la devolución de llamada set_id: en los documentos de openSSL está escrito que si no implementamos esta función, entonces se usa una implementación predeterminada: ¿sabe cuál es la ventana predeterminada de imn? ¿Es diferente de tu implementación? y ¿sabes algo sobre las devoluciones de llamadas de los bloqueos dinámicos? ¿Tenemos que implementarlo también? ¡Gracias! – RRR

+0

@RRR Encabezo todas sus preguntas. Los bloqueos dinámicos parecen bastante nuevos en la API, y la mayoría de los ejemplos de subprocesamiento con OpenSSL en Internet están desactualizados. Parece que todavía debería funcionar para permitir la compatibilidad con versiones anteriores, pero los dynlocks podrían agregar un mejor rendimiento, esa es mi corazonada. – Tosha