J'ai créé un pthread et installé un gestionnaire de signaux à l'intérieur de celui-ci, de la même manière que nous le faisons dans la fonction main( )
. Le gestionnaire de signal du thread est une fonction distincte. Étonnamment, cela ne fonctionne pas, c'est-à-dire que le gestionnaire de signal du thread n'est pas capable de capter les signaux.
Voici le code:
#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);
}
Production:
Erreur de segmentation (ce qui signifie que le signal n'est pas capté par le gestionnaire)
Il y a plusieurs problèmes avec votre code:
ptr
n'est pas initialisé, donc toutes les parties ptr->
planteront le programmepthread_kill()
immédiatement, très probablement avant l'installation du gestionnaire de signal, et dans un thread (qui a un comportement non spécifié)printf()
à partir d'un gestionnaire de signaux, ce qui n'est pas garanti de fonctionner (voir man 7 signal
pour une liste des fonctions sûres)Cela fonctionnera beaucoup mieux, bien que vous ayez toujours besoin d'une bonne synchronisation des threads, et comme indiqué ailleurs, vous devez utiliser 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: installez sighandler dans le thread principal
Je crois que le cœur du problème est que les signaux sont transmis au processus dans son ensemble, plutôt qu'aux threads individuels. Généralement, un seul thread est désigné pour gérer tous les signaux; tous les autres threads (y compris le thread principal) doivent bloquer les signaux en utilisant pthread_sigmask()
.
Vous pouvez définir le masque pour bloquer tous les signaux, démarrer votre thread de traitement de signal, démasquer les signaux que vous souhaitez gérer, puis revenir dans le thread principal, démarrer tous les autres threads dont vous avez besoin. Ils hériteront du masque "bloquer tous les signaux" du thread principal.
Par ailleurs, il est temps de s'éloigner de signal(3)
et de passer à sigaction(2)
, qui a une sémantique fiable et est mieux standardisé. (Et donc plus portable.)
Le seul problème avec votre code que personne n'a encore mentionné est que, lors du blocage du signal (et de la livraison, si vous utilisez pthread_kill
ou raise
) sont par thread, les gestionnaires de signaux sont par processus. Cela signifie qu'ils sont un très mauvais mécanisme de communication inter-threads, surtout si votre code sera utilisé comme code de bibliothèque, car il est extrêmement mauvais pour une bibliothèque de modifier les gestionnaires de signal de l'appelant.
Notez également que l'utilisation de gestionnaires de signaux pour la communication entre les threads a des performances sous-optimales par rapport à d'autres méthodes de signalisation des threads comme les variables de condition ou les barrières, car il y a au moins une transition utilisateur-noyau-utilisateur supplémentaire (lorsque le gestionnaire de signaux revient).