2009-11-11 6 views
16

Nous avons une bibliothèque qui traite de nombreux aspects du signalement des erreurs. J'ai été chargé de porter cette bibliothèque sur Linux. Lors de l'exécution de ma petite suite de tests, l'un des tests a échoué. Une version simplifiée du test apparaît ci-dessous.Lancement d'une exception depuis un gestionnaire de signal

// Compiler: 4.1.1 20070105 RedHat 4.1.1-52 
// Output: Terminate called after throwing an instance of 'int' abort 

#include <iostream> 
#include <csignal> 
using namespace std; 

void catch_signal(int signalNumber) 
{ 
    signal(SIGINT, SIG_DFL); 
    throw(signalNumber); 
} 

int test_signal() 
{ 
    signal(SIGINT, catch_signal); 

    try 
    { 
     raise(SIGINT); 
    } 
    catch (int &z) 
    { 
     cerr << "Caught exception: " << z << endl; 
    } 
    return 0; 
} 

int main() 
{ 
    try 
    { 
     test_signal(); 
    } 
    catch (int &z) 
    { 
     cerr << "Caught unexpected exception: " << z << endl; 
    } 
    return 0; 
} 

Mon attente est que le Caught exception: le message sera affiché. Qu'est-ce qui se passe réellement, c'est que le programme se termine car aucun gestionnaire de catch semble être présent pour l'int lancé.

Il y a quelques questions sur les SO qui semblent liées. J'ai trouvé un certain nombre de pages Google liées. La «sagesse» semble se résumer à.

  1. Ya ne peut pas lancer des exceptions de gestionnaires de signaux, provoque le gestionnaire signal de fonctionne avec sa propre pile, donc il n'y a pas gestionnaires définis sur elle.
  2. Ya peut jeter des exceptions à partir des gestionnaires de signaux, il suffit de reconstruire un faux cadre sur la pile, et vous êtes prêt à partir.
  3. Ya, on le fait tout le temps. Cela fonctionne pour moi sur la plate-forme X
  4. Ya, qui était disponible avec gcc, mais ne semble pas fonctionner plus. Essayez l'option -fnon-call-exceptions, peut-être que cela fonctionnera

    Le code fonctionne comme prévu sur nos compilateurs/environnements AIX/TRU64/MSVC. Il échoue dans notre environnement Linux.


Je cherche des suggestions pour aider à résoudre ce problème afin que le comportement bibliothèque Linux correspondra à mes autres plates-formes, ou une sorte ou solution de contournement qui pourrait atteindre le même type de fonctionnalité.
La suppression du signal de vidage du programme sur le noyau n'est pas une option viable.

+0

[Cet article] (http://www.ibm.com/developerworks/library/l-cppexcep/) prétend que cela fonctionne; il mentionne un correctif peut être nécessaire que cela ne semble pas expliquer cependant. –

Répondre

13

Les signaux sont totalement différent des exceptions C++. Vous ne pouvez pas utiliser un bloc try/catch C++ pour gérer un signal. Plus précisément, les signaux sont un concept POSIX, pas un concept de langage C++. Les signaux sont fournis de manière asynchrone à votre application par le noyau, tandis que les exceptions C++ sont des événements synchrones définis par la norme C++.

Vous êtes assez limité dans ce que vous pouvez faire de façon portative dans un gestionnaire de signal POSIX. Une stratégie commune consiste à avoir un indicateur global de type sig_atomic_t qui sera défini sur 1 dans le gestionnaire de signaux, puis éventuellement sur longjmp sur le chemin d'exécution approprié.

Voir here pour l'aide à l'écriture de gestionnaires de signaux appropriés.

+8

Définir un drapeau est la chose la plus sûre à faire. C'est à dire, si vous devez utiliser un gestionnaire de signal du tout. La livraison d'un signal est très chère. Le noyau doit construire un cadre de pile spécial et y insérer toutes sortes de contextes spécifiques à la machine. Votre gestionnaire de signal peut s'exécuter en toute sécurité dans ce cadre, mais il n'y a aucune garantie quant à ce qui existe plus haut dans la pile. C'est effectivement pourquoi vous ne pouvez pas lancer ... le compilateur n'a aucun moyen de générer du code de gestion des exceptions pour ce contexte. –

+3

Dans une certaine mesure, la sécurité est moins préoccupante. L'exception à lancer est précisément d'interrompre le programme de manière significative. Il n'y a aucune intention d'essayer de redémarrer les opérations. – EvilTeach

+2

Ok, alors vous êtes probablement bloqué avec 'longjmp' pour bloquer le code du gestionnaire. Si ce code est normalement dans un bloc 'catch', vous pouvez le factoriser à une fonction avec un lien' C'. –

4

Je masquerais tous les signaux dans chaque thread, sauf celui qui attendrait les signaux avec sigwait (). Ce fil peut gérer des signaux sans restriction, par ex. jeter des exceptions ou utiliser d'autres mécanismes de communication.

+0

Ce programme n'est pas multithread. – EvilTeach

+2

@EvilTeach, néanmoins je serais d'accord avec Bastien Leonard. Avoir une file d'attente séparée sur 'sigwait' vous donne la plus grande flexibilité quand il s'agit de traiter les signaux. Sinon, vous cherchez essentiellement à utiliser des flags globaux et un 'longjmp', ce qui n'est pas très joli. –

+0

@Charles pop dans votre suggestion longjmp comme une réponse – EvilTeach

4

La suppression d'un gestionnaire de signal n'est probablement pas une bonne idée, car la pile n'est pas nécessairement configurée de la même manière que pour les appels de fonction, donc le déroulement d'un gestionnaire de signal peut ne pas fonctionner comme prévu.

Une note importante doit être prise pour tout registre utilisé par l'ABI C++ qui est sauvegardé et réutilisé par le mécanisme de gestion de signal.

1

google g ++ Option

-fnon-call-exceptions 

C'est essentiellement ce que vous voulez. Je pense que cela a été développé en raison de la pression de la pomme pour leur OS. Je ne suis pas certain de la façon dont il est pris en charge sur LINUX. Et je ne suis pas certain si l'on peut attraper SIGINT - mais tous les signaux déclenchés par le CPU (aeh exceptions) peuvent être interceptés. Les codeurs qui ont besoin de cette fonctionnalité (et qui ne se soucient pas d'idéologie) devraient créer une pression sur la communauté des développeurs LINUX pour qu'elle soit également supportée sur LINUX un jour - après avoir été supportée par Windows depuis près de deux décennies.

+1

Cela ne fonctionne pas. – EvilTeach

+1

Cela n'a pas fonctionné sur le Raspberry Pi. ** fait pression sur la communauté de développeurs LINUX ** –

5

Ce code illustre une technique qui déplace le rejet de l'exception hors du gestionnaire de signal dans le code. Mes remerciements à Charles pour l'idée.

#include <iostream> 
#include <csignal> 
#include <csetjmp> 

using namespace std; 

jmp_buf gBuffer;  // A buffer to hold info on where to jump to 

void catch_signal(int signalNumber) 
{ 
    //signal(SIGINT, SIG_DFL);   // Switch to default handling 
    signal(SIGINT, catch_signal);  // Reactivate this handler. 

    longjmp    // Jump back into the normal flow of the program 
    (
     gBuffer,  // using this context to say where to jump to 
     signalNumber // and passing back the value of the signal. 
    ); 
} 


int test_signal() 
{ 
    signal(SIGINT, catch_signal); 

    try 
    { 
     int sig; 
     if ((sig = setjmp(gBuffer)) == 0) 
     { 
      cout << "before raise\n"; 
      raise(SIGINT); 
      cout << "after raise\n"; 

     } 
     else 
     { 
      // This path implies that a signal was thrown, and 
      // that the setjmp function returned the signal 
      // which puts use at this point. 

      // Now that we are out of the signal handler it is 
      // normally safe to throw what ever sort of exception we want. 
      throw(sig); 
     } 
    } 
    catch (int &z) 
    { 
     cerr << "Caught exception: " << z << endl; 
    } 

    return 0; 
} 

int main() 
{ 
    try 
    { 
     test_signal(); 
    } 
    catch (int &z) 
    { 
     cerr << "Caught unexpected exception: " << z << endl; 
    } 
    return 0; 
} 
+5

'setjmp' et' longjmp' ne sont pas compatibles avec les exceptions et RAII ('ctors/dtors'), cependant.:(Vous aurez probablement des fuites de ressources avec ceci: – PSkocik

+0

@PSkocik - ya. Je sais, c'est bon, nous voulons essentiellement un message enregistré de quelque sorte et une application abandonnée. – EvilTeach