web-dev-qa-db-fra.com

Gestion du signal dans les fils de lecture

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)

22
kingsmasher1

Il y a plusieurs problèmes avec votre code:

  • ptr n'est pas initialisé, donc toutes les parties ptr-> planteront le programme
  • vous appelez pthread_kill() immédiatement, très probablement avant l'installation du gestionnaire de signal, et dans un thread (qui a un comportement non spécifié)
  • vous appelez 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

26
sam hocevar

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.)

10
sarnold

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).

4
R..