2011-03-12 4 views
19

Ho creato un pthread e installato un gestore di segnale al suo interno, proprio come facciamo nella funzione main(). Il gestore di segnale del thread è una funzione separata. Sorprendentemente, non funziona, cioè il gestore del segnale del thread non è in grado di rilevare i segnali.Gestione del segnale in pthreads

Ecco il codice:

#include <pthread.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <stdio.h> 
#include <signal.h> 

typedef struct data 
{ 
char name[10]; 
int age; 
}data; 

void sig_func(int sig) 
{ 
printf("Caught signal: %d\n",sig); 
signal(SIGSEGV,sig_func); 
} 

void func(data *p) 
{ 
printf("This is from thread function\n"); 
signal(SIGSEGV,sig_func); // Register signal handler inside thread 
strcpy(p->name,"Mr. Linux"); 
p->age=30; 
sleep(2); // Sleep to catch the signal 
} 

int main() 
{ 
pthread_t tid; 
pthread_attr_t attr; 
data *ptr; 

pthread_attr_init(&attr); 
pthread_create(&tid,&attr,(void*)func,ptr); 
pthread_kill(tid,SIGSEGV); 

pthread_join(tid,NULL); 
printf("Name:%s\n",ptr->name); 
printf("Age:%d\n",ptr->age); 
} 

uscita:

Segmentation fault (che significa che il segnale non è catturato dal gestore)

+7

Hai scoperto che [thread e segnali non interagiscono correttamente] (http: // stackoverflow.it/questions/2575106/posix-threads-and-signals) :) – sarnold

+1

Per iniziare, e per continuare ciò che @sarnold ha detto, stai usando l'API sbagliata. Non usare 'signal()'. Dalla pagina man (* read it *): "Gli effetti di signal() in un processo con multithreading non sono specificati." Inizia a leggere i documenti a man 2 sigaction. – rlibby

+0

@rlibby: Quindi dovrei usare la "struct sigaction" o "sigevent structure" per catturare i segnali, vuoi dire? – kingsmasher1

risposta

24

Ci sono diversi problemi con il tuo codice:

  • ptr non è inizializzata, in modo che tutti i ptr-> parti saranno in crash il programma
  • si sta chiamando pthread_kill() immediatamente, molto probabile prima che il gestore di segnale sia stato installato, e in una discussione (che ha un comportamento non specificato)
  • chiami printf() da un gestore di segnale, che non è garantito per funzionare (vedi man 7 signal per un elenco di funzioni sicure)

Questo funziona molto meglio, anche se si sarebbe ancora bisogno di una corretta sincronizzazione dei thread, e come detto altrove, è necessario utilizzare sigaction():

#include <pthread.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <stdio.h> 
#include <signal.h> 

typedef struct data 
{ 
char name[10]; 
int age; 
}data; 

void sig_func(int sig) 
{ 
write(1, "Caught signal 11\n", 17); 
signal(SIGSEGV,sig_func); 
} 

void func(data *p) 
{ 
fprintf(stderr, "This is from thread function\n"); 
strcpy(p->name,"Mr. Linux"); 
p->age=30; 
sleep(2); // Sleep to catch the signal 
} 

int main() 
{ 
pthread_t tid; 
pthread_attr_t attr; 
data d; 
data *ptr = &d; 

signal(SIGSEGV,sig_func); // Register signal handler before going multithread 
pthread_attr_init(&attr); 
pthread_create(&tid,&attr,(void*)func,ptr); 
sleep(1); // Leave time for initialisation 
pthread_kill(tid,SIGSEGV); 

pthread_join(tid,NULL); 
fprintf(stderr, "Name:%s\n",ptr->name); 
fprintf(stderr, "Age:%d\n",ptr->age); 
} 

Edit: installare sighandler nel thread principale

+0

Grazie mille, ha funzionato. Quindi pensi che solo l'inizializzazione del puntatore sia stata un problema? Ma se non porto alcun segnale il codice funziona senza errori. – kingsmasher1

+0

Anche la registrazione del gestore di segnale sul main, invece della funzione thread funziona. Perché è così? – kingsmasher1

+1

@kingsmasher: i due problemi principali sono stati l'inizializzazione del puntatore e il fatto che tu abbia chiamato 'pthread_kill()' prima che il thread secondario avesse il tempo di impostare il gestore del segnale. Senza queste due correzioni, il programma non avrebbe potuto funzionare come previsto. Gli altri problemi sono i problemi di portabilità che causano comportamenti imprevisti su vari gusti di Linux o Unix, quindi è necessario affrontarli. Fino ad allora, considera che funziona solo per caso. –

10

ritengo il nucleo del problema è che i segnali vengono inviati al processo nel suo complesso, piuttosto che ai singoli thread. Comunemente, un singolo thread è nominato per gestire tutti i segnali; tutti gli altri thread (incluso il thread principale) devono essere block the signals using pthread_sigmask().

È possibile impostare la maschera per bloccare tutti i segnali, avviare il filo del gestore di segnale, smascherare i segnali che si desidera gestire e quindi tornare indietro nel thread principale, avviare tutti gli altri thread necessari. Essi erediteranno la maschera "blocca tutti i segnali" dal thread principale.

Per inciso, è ora di allontanarsi da signal(3) e passare a sigaction(2), che ha semantica affidabile ed è meglio standardizzata. (E quindi più portabile.)

+0

@sarnold: hai detto: "credo che il nocciolo del problema sia che i segnali vengono inviati al processo nel suo insieme, piuttosto che ai singoli thread" ma se uso pthread_kill (thread_id, signal_no) il segnale che penso sia consegnato a lui specifico filo. – kingsmasher1

+0

@ Kingsmasher1, non sono sicuro che sia vero :) ma la risposta di @Sam Hocevar è fantastica. Sono tentato di cancellare la mia risposta, ma potrebbe essere ancora utile. – sarnold

+0

@sarnold: Sì, ha funzionato troppo :) Ho appena eseguito il suo codice. – kingsmasher1

4

L'unico problema con il codice che nessuno ha ancora menzionato è che, mentre il blocco del segnale (e la consegna, se si utilizza pthread_kill o raise) sono per-thread, i gestori di segnale sono per processo. Ciò significa che sono un pessimo meccanismo per la comunicazione tra thread, soprattutto se il codice verrà mai utilizzato come codice di libreria, dal momento che è un comportamento estremamente negativo per una libreria modificare i gestori di segnale del chiamante.

Si noti inoltre che l'utilizzo di gestori di segnale per la comunicazione tra thread ha prestazioni non ottimali rispetto ad altri metodi di segnalazione di thread come variabili di condizione o barriere, poiché c'è almeno una transizione utente-utente-utente aggiuntiva (quando il gestore di segnale restituisce).

+0

Grazie per l'accurata indagine e i vostri commenti. Hai detto "mentre il blocco del segnale (e la consegna, se usi pthread_kill o raise) sono per-thread, i gestori di segnale sono per processo." Se registriamo il gestore di segnale all'interno del thread, non pensi che sia con riferimento solo a quel thread? Qual è la differenza tra l'aggiunta di gestori di segnale (per la precisione, registrazione dei gestori di segnale) all'interno della funzione principale e all'interno dei thread? So che se ci si registra all'interno di main() ci si occupa anche dei thread, poiché i thread condividono il codice main(). – kingsmasher1

+0

Quindi, se registriamo il gestore all'interno della funzione thread, è solo specifico per quel thread? – kingsmasher1

+0

@ kingsmahser1: no, niente indica che l'archiviazione per 'signal' è o dovrebbe essere thread-local. Si dovrebbe presumere che la registrazione di un gestore di segnale abbia lo stesso effetto indipendentemente dal thread corrente. –