2010-10-20 9 views
0

Je migre une application multi-thread de HP-UX vers Solaris et jusqu'à présent, tout est OK sauf pour une chose! L'application a un thread qui gère les signaux et, quand certains d'entre eux sont reçus, il exécute un nettoyage (enregistrement, tuer les processus enfant et ainsi de suite).Comportement des threads POSIX différent entre HP-UX et Solaris 10

J'ai réduit le code autant qu'il était possible de faire un exemple simple montrant en quelque sorte le problème:

#include <pthread.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <unistd.h> 
#include <signal.h> 
#include <synch.h> 
#include <iostream> 
#include <unistd.h> 

using namespace std; 

pthread_t  m_signalHandlerThread; 
sigset_t  m_signalSet; 

void signalHandler() 
{ 
    while (true) 
    { 
     cout << "SigWait..." << endl; 
     sigwait(&m_signalSet, &sig); 
     cout << "Signal!! : " << sig << endl; 

     break; 
    } 

    cout << "OUT" << endl; 
} 

void* signalHandlerThreadFunction(void* arg) 
{ 
    signalHandler(); 

    return (void*)0; 
} 


int main() 
{ 
    sigemptyset(&m_signalSet); 
    sigaddset(&m_signalSet, SIGQUIT);    //kill -QUIT 
    sigaddset(&m_signalSet, SIGTERM);    //kill 
    sigaddset(&m_signalSet, SIGINT);    //ctrl-C 
    sigaddset(&m_signalSet, SIGHUP);    //reload config 

    if (pthread_create(&m_signalHandlerThread, NULL, signalHandlerThreadFunction, NULL)) 
    { 
     cout << "cannot create signal handler thread, system shut down.\n" << endl; 
    } 

    int iTimeout = 0; 
    while (1) 
    { 
     if (iTimeout >= 10) 
      break; 

     sleep(1); 
     iTimeout++; 
     cout << "Waiting... " << iTimeout << endl; 
    } 

    cout << "END" << endl; 

    exit (0); 
} 

En utilisant la compilation des lignes de commande: Solaris:

CC -m64 -g temp.cpp -D_POSIX_PTHREAD_SEMANTICS -lpthread 

HP- UX:

/opt/aCC/bin/aCC +p +DA2.0W -AA -g -z -lpthread -mt -I/usr/include temp.cpp  

Exécution des deux applications, le comportement (pression g CTRL + C alors que dans les 10 secondes en boucle):

HP-UX:

./a.out 

SigWait... 
Waiting... 1 
Waiting... 2 
Signal!! : 2 <---- CTRL + C 
OUT 
Waiting... 3 
Waiting... 4 <---- CTRL + C again to terminate 

Solaris:

./a.out 

SigWait... 
Waiting... 1 
Waiting... 2 <---- CTRL + C 
^C 

Toute aide sera plus que bienvenu puisque je suis déjà déchirer mon les cheveux (pas beaucoup à gauche) :)!

Merci!

Répondre

-1

Ceci est une façon plutôt peu orthodoxe de gérer les signaux. Si vous voulez marier les signaux et les threads, un meilleur choix serait d'avoir l'signal handlers habituel d'où le signal est sérialisé en interne à un autre thread qui est responsable de la gestion réelle de l'événement.

C'est également une meilleure option, car il est indéfini quel thread dans une application MT reçoit le signal. Les threads qui n'ont pas le signal bloqué peuvent le recevoir. Si vous avez 2 threads (et que vous avez deux threads dans l'exemple) alors n'importe lequel des threads pourrait obtenir le SIGINT.

Vous pouvez vérifier sigprocmask() pour indiquer à OS que SIGINT doit être bloqué dans un thread. Cela devrait être fait pour chaque thread, IIRC même celui appelant sigwait().


Édition1. En fait, je me trompe sur le "devrait être fait pour chaque thread" peu ci-dessus. Un nouveau thread hérite de son masque de signal du thread en cours. J'ai réalisé que cela ne peut pas être vrai parce que cela aurait introduit la condition de concurrence: le signal arrive au moment où le nouveau thread a été créé mais n'a pas encore défini son masque de signal. En d'autres termes, il suffit de régler le masque de signal dans le thread principal.

+0

Merci! Cela a résolu mon problème, même dans l'ensemble de l'application. Je ne comprends tout simplement pas pourquoi cela fonctionne sur HP-UX ... peut-être que dans cette implémentation de thread tous les threads reçoivent le signal? – JoaoSantos

+0

"peut-être dans cette implémentation de thread tous les threads reçoivent le signal?" l'application a reçu le signal - mais il peut être manipulé dans n'importe quel thread * random * où il pourrait être manipulé. HP-UX a peut-être remarqué qu'un thread dans votre application utilise sigwait() alors que Solaris n'a pas été dérangé. Signaux vs threads est mieux décrit comme une zone grise où vous ne voulez vraiment pas expérimenter. Même POSIX ne peut pas décrire le comportement dans son intégralité puisque les détails les plus fins diffèrent grandement d'un OS à l'autre. – Dummy00001

+0

Je donne -1 pour cette réponse. Cette réponse est fausse en ce sens que vous ne pouvez pas sérialiser le signal du gestionnaire de signal classique vers un autre thread ou structure de données. Vous devrez utiliser un verrouillage à l'intérieur du gestionnaire de signal et ce n'est pas possible. Il y a très peu de choses que vous pouvez faire dans un gestionnaire de signal, l'utilisation de primitives de verrouillage n'en fait pas partie. – wilx

4

Il n'est pas spécifié lequel de vos 2 threads va gérer SIGINT. Si vous n'avez besoin que d'un de vos threads pour gérer le signal, vous devez bloquer ce signal dans tous les autres threads que vous avez.

+0

Votre réponse m'a orienté dans la bonne direction. Merci. – JoaoSantos

1

Vous devez bloquer les signaux vers d'autres threads en utilisant pthread_sigmask. cette page contient également un exemple pour un programme avec un thread de traitement de signal.

+0

Vous avez raison sur la fonction à utiliser. J'ai accepté le précédent car il a été posté avant le vôtre bien qu'il mentionne l'appel pour une fonction à un seul thread. – JoaoSantos

+0

@JoaoSantos: 'pthread_sigmask()' est équivalent à 'sigprocmask()'. Il n'y a pas de fonctions de signal "single-threaded" ou "multi-threaded" - certaines fonctions ont un comportement défini dans les applications MT (comme les deux) ou indéfinies (par exemple 'sigpause()' ne peut être utilisé que dans les applications ST/shouldn ne pas être utilisé du tout). – Dummy00001

1

A propos de la seule façon comment gérer des signaux bien dans l'application multithread est de faire ce qui suit:

  1. bloc tous les signaux main() tôt, avant que d'autres threads donné naissance, en utilisant pthread_sigmask().
  2. Faire apparaître un fil de traitement des signaux. Utilisez sigwait() ou sigwaitinfo() pour gérer les signaux dans une boucle simple.

De cette façon, aucun thread autre que celui dédié à la gestion du signal n'obtiendra les signaux. De plus, puisque la distribution du signal est synchrone de cette façon, vous pouvez utiliser toutes les facilités de communication inter-thread que vous avez, contrairement aux gestionnaires de signaux classiques.