2014-07-08 13 views
11

Ho problemi a ottenere thread per ottenere i segnali corretti.Pthreads e segnali Unix: gestori di segnale per thread

Ad esempio,

ho inizio un filo conduttore (tid 1).

Quindi, imposta un gestore di segnale per SIGUSR1 a function1(), utilizzando signal(2).

Il thread principale crea un nuovo thread, con tid 2.

in filo 2, registro un gestore di segnale per SIGUSR1-function2() utilizzando signal(2).

Il thread 1 crea quindi una filettatura 3 (tid 3).

Da filo 3, io uso pthread_kill(1, SIGUSR1) di inviare un segnale al filo 1.

Tuttavia, function2() viene chiamato, non function1().

Questo comportamento è inteso, o c'è qualcosa che devo cambiare per far funzionare questi gestori di segnale?

Modifica: ho eseguito un po 'di debug e risulta che il segnale viene inviato al thread 1, tuttavia function2() viene chiamato dal thread 1 per qualche motivo. C'è una soluzione per questo?

+3

il segnale è una chiamata per processo, non una per thread, se la si chiama imposta il gestore per tutti i thread nel processo. Segnali e thread è un argomento complesso. Potresti stare meglio chiedendo cosa tu realmente cosa fare. –

+3

L'uso di 'signal' in un programma con multithreading è essenzialmente un comportamento non definito. Lo dice nel manuale. –

+0

Codice sorgente per favore. –

risposta

7

Oltre a alk's answer:

È possibile utilizzare una funzione per thread puntatore per scegliere quale funzione viene eseguita quando viene consegnato un determinato segnale, in modo per-thread.

Nota: i segnali vengono consegnati a qualsiasi thread che non blocca esplicitamente la consegna. Questo non lo cambia. È comunque necessario utilizzare pthread_kill() o meccanismi simili per indirizzare il segnale a un thread specifico; i segnali che vengono generati o inviati al processo (invece di un thread specifico), saranno comunque gestiti da un thread casuale (tra quelli che non lo bloccano).

Non riesco a pensare a nessun caso d'uso in cui preferirei personalmente questo approccio; finora c'è sempre stato un altro modo, qualcos'altro più facile e migliore.Quindi, se stai pensando di implementare qualcosa di simile per un'applicazione reale, ti preghiamo di fare un passo indietro e di riconsiderare la logica dell'applicazione.

Ma, dal momento che la tecnica è possibile , ecco come potrei implementarlo:

#include <signal.h> 

/* Per-thread signal handler function pointer. 
* Always use set_thread_SIG_handler() to change this. 
*/ 
static __thread void (*thread_SIG_handler)(int, siginfo_t *, void *) = (void *)0; 

/* Process-wide signal handler. 
*/ 
static void process_SIG_handler(int signum, siginfo_t *info, void *context) 
{ 
    void (*func)(int, siginfo_t *, void *); 

#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) 
    func = __atomic_load_n(&thread_SIG_handler, __ATOMIC_SEQ_CST); 
#else 
    func = __sync_fetch_and_add(&thread_SIG_handler, (void *)0); 
#endif 

    if (func) 
     func(signum, info, context); 
} 

/* Helper function to set new per-thread signal handler 
*/ 
static void set_thread_SIG_handler(void (*func)(int, siginfo_t *, void *)) 
{ 
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) 
    __atomic_store_n(&thread_SIG_handler, func, __ATOMIC_SEQ_CST); 
#else 
    void (*oldfunc)(int, siginfo_t *, void *); 
    do { 
     oldfunc = thread_SIG_handler; 
    } while (!__sync_bool_compare_and_swap(&thread_SIG_handler, oldfunc, func)); 
#endif 
} 

/* Install the process-wide signal handler. 
*/ 
int install_SIG_handlers(const int signum) 
{ 
    struct sigaction act; 
    sigemptyset(&act.sa_mask); 
    act.sa_sigaction = process_SIG_handler; 
    act.sa_flags = SA_SIGACTION; 
    if (sigaction(signum, &act, NULL)) 
     return errno; 
    return 0; 
} 

Mi piace il sopra, perché non richiede pthreads, ed è molto robusta e affidabile. A parte il caos visivo dovuto alla logica del preprocessore per selezionare quale stile di built-in atomico vengono usati, è anche molto semplice, se si guarda attentamente.

GCC 4.7 e versioni successive forniscono C++ 11-like __atomic built-ins, versioni GCC precedenti e altri compilatori (ICC, Pathscale, Portland Group) forniscono __sync legacy built-ins. Analogamente, lo __thread keyword per l'archiviazione locale dei thread dovrebbe essere disponibile in tutti i sistemi POSIX-y correnti.

Se si dispone di un sistema arcaico, o insistere su conformità agli standard, il seguente codice dovrebbe avere un comportamento più o meno equivalente:

#include <pthread.h> 
#include <signal.h> 
#include <errno.h> 

static pthread_key_t thread_SIG_handler_key; 

static void process_SIG_handler(int signum, siginfo_t *info, void *context) 
{ 
    void (*func)(int, siginfo_t *, void *); 

    *((void **)&func) = pthread_getspecific(thread_SIG_handler_key); 
    if (func) 
     func(signum, info, context); 
} 

static int set_thread_SIG_handler(void (*func)(int, siginfo_t *, void *)) 
{ 
    sigset_t block, old; 
    int result; 

    sigemptyset(&block); 
    sigaddset(&block, SIG); /* Use signal number instead of SIG! */ 
    result = pthread_sigmask(SIG_BLOCK, &block, &old); 
    if (result) 
     return errno = result; 

    result = pthread_setspecific(thread_SIG_handler_key, (void *)func); 
    if (result) { 
     pthread_sigmask(SIG_SETMASK, &old, NULL); 
     return errno = result; 
    } 

    result = pthread_sigmask(SIG_SETMASK, &old, NULL); 
    if (result) 
     return errno = result; 

    return 0; 
} 

int install_SIG_handlers(const int signum) 
{ 
    struct sigaction act; 
    int result; 

    result = pthread_key_create(&thread_SIG_handler_key, NULL); 
    if (result) 
     return errno = result; 

    sigemptyset(&act.sa_mask); 
    act.sa_sigaction = process_SIG_handler; 
    act.sa_flags = SA_SIGACTION; 
    if (sigaction(signum, &act, NULL)) 
     return errno; 

    return 0; 
} 

penso che il più vicino equivalente vita reale a codice come questo che ho in realtà mai usato, è quello in cui ho usato un segnale in tempo reale (SIGRTMIN+0) bloccato in tutto tranne un thread, come un riflettore: ha inviato un altro segnale in tempo reale (SIGRTMIN+1) a un numero di thread di lavoro, per interrompere l'I/O di blocco. (È possibile fare questo con un unico segnale in tempo reale, ma il modello a due segnale è più semplice da implementare e facile da mantenere.)

Tale riflessione del segnale o fanout volte è utile, e non è molto diverso da questo approccio. Abbastanza diverso da giustificare la sua domanda, però, se qualcuno è interessato.

+0

Hai assolutamente ragione a ripensare il mio design. Ho scelto di usare pthread_cancel perché mi sembrava adatto alle mie esigenze. Tuttavia, potrebbe esserci un piccolo problema nelle tue inserzioni. 'pthread_getspecific()' non è sicuro per il segnale asincrono secondo la pagina man, e non credo che sia l'accesso allo storage '__thread'. – tomKPZ

+0

@ user1887231: In pratica, [sono] (http://sourceware.org/ml/libc-alpha/2012-06/msg00372.html). Gli standard non hanno ancora affrontato il problema delle variabili specifiche del thread con segnale asincrono. Almeno con gli strumenti GNU, entrambe le variabili 'pthread_getspecific()' e '__thread' sono async-signal safe * in pratica *. (Qui, solo il gestore di segnale * legge * la variabile specifica del thread, quindi i casi di "fuori-TLS-storage" non si applicano.) Se ti preoccupi di questo, fammelo sapere e creerò un rimedio (Penso di conoscere un modo). –

4

Non è possibile installare gestori di segnale "per-thread".

Da man 7 signal (enfasi da me):

La disposizione segnale è un attributoper processo: in un'applicazione multithreading, la disposizione di un particolare segnale è la stessa per tutte le filettature.

E tuttavia è possibile dirigere ciascun segnale di tipo a un thread diverso, mascherando la ricezione di qualsiasi numero di segnali-tipo su una base "per-filo".

Su come dirigere una serie di segnali tipi ad una discussione specifica come si potrebbe avere un'occhiata a questa risposta: https://stackoverflow.com/a/20728819/694576

Problemi correlati