Question

J'étais sur le point d'ajouter un gestionnaire de signal supplémentaire à une application que nous avons ici et j'ai remarqué que l'auteur avait utilisé sigaction () pour configurer les autres gestionnaires de signal. J'allais utiliser signal () . Pour suivre la convention, je devrais utiliser sigaction () , mais si je devais écrire à partir de rien, lequel devrais-je choisir?

Était-ce utile?

La solution

Utilisez sigaction () à moins que vous avez des raisons très convaincantes de ne pas le faire.

L'interface signal () a l’antiquité (et donc la disponibilité) en sa faveur, et il est défini dans la norme C. Néanmoins, il a un certain nombre de caractéristiques indésirables que sigaction () évite - sauf si vous utilisez les indicateurs explicitement ajoutés à sigaction () pour lui permettre de simuler fidèlement l'ancien signal () comportement.

  1. La fonction signal () ne bloque pas (nécessairement) l'arrivée d'autres signaux pendant l'exécution du gestionnaire en cours; sigaction () peut bloquer d'autres signaux jusqu'au retour du gestionnaire actuel.
  2. La fonction signal () (généralement) réinitialise l'action du signal sur SIG_DFL (valeur par défaut) pour presque tous les signaux. Cela signifie que le gestionnaire signal () doit être réinstallé comme première action. Il ouvre également une fenêtre de vulnérabilité entre le moment où le signal est détecté et le gestionnaire est réinstallé. Lors de l’arrivée d’une deuxième instance du signal, le comportement par défaut (généralement la fin, parfois avec un préjudice - core dump) se produit. / li>
  3. Le comportement exact de signal () varie selon les systèmes & # 8212; et les normes permettent ces variations.

Ce sont généralement de bonnes raisons d'utiliser sigaction () au lieu de signal () . Toutefois, l’interface de sigaction () est indéniablement plus complexe.

Quel que soit le type que vous utilisez, ne vous laissez pas tenter par les interfaces de signal alternatives telles que sighold () , sigignore () , sigpause () et sigrelse () . Ils sont théoriquement des alternatives à sigaction () , mais ils sont à peine normalisés et sont présents dans POSIX pour des raisons de compatibilité ascendante plutôt que pour une utilisation sérieuse. Notez que la norme POSIX dit que leur comportement dans les programmes multithreads n’est pas défini.

Les programmes et signaux multithreads sont une toute autre histoire compliquée. autant que je sache, signal () et sigaction () sont acceptables dans les applications multithreads.

Cornstalks observe :

  

La page de manuel Linux de signal () dit:

     

& nbsp; & nbsp; Les effets de signal () dans un processus multithread ne sont pas spécifiés.

     

Ainsi, je pense que sigaction () est le seul qui peut être utilisé en toute sécurité dans un processus multithread.

C'est intéressant. La page de manuel Linux est plus restrictive que POSIX dans ce cas. POSIX spécifie pour signal () :

  

Si le processus est multi-threadé ou mono-thread et qu'un gestionnaire de signal est exécuté autrement que comme résultat de:

     
      
  • Le processus appelant abort () , raise () , kill () , pthread_kill () , ou sigqueue () pour générer un signal non bloqué
  •   
  • Un signal en attente débloqué et remis avant l'appel qui l'a débloqué est renvoyé
  •   
     

le comportement n'est pas défini si le gestionnaire de signal fait référence à un objet autre que errno avec une durée de stockage statique autre qu'en affectant une valeur à un objet déclaré volatile sig_atomic_t , ou si le gestionnaire de signaux appelle une fonction définie dans la présente norme autre que l’une des fonctions répertoriées dans Concepts de signal .

Donc, POSIX spécifie clairement le comportement de signal () dans une application multithread.

Néanmoins, sigaction () doit être préféré dans toutes les circonstances & # 8212; et le code multithread portable doit utiliser sigaction () , sauf en cas de raison impossible, (par exemple, "utiliser uniquement les fonctions définies par la norme C" et oui, le code C11 peut être multi-threadé). C’est essentiellement ce que dit également le paragraphe d’ouverture de cette réponse.

Autres conseils

Ce sont différentes interfaces pour les installations de signalisation du système d'exploitation. Si possible, préférez utiliser sigaction pour signaler le signal () car il a un comportement défini par la mise en oeuvre (souvent sujet à la race) et se comporte différemment sous Windows, OS X, Linux et autres systèmes UNIX.

Voir note de sécurité pour plus de détails.

Pour moi, cette ligne ci-dessous était suffisante pour décider:

  

La fonction sigaction () fournit un   plus complet et fiable   mécanisme de contrôle des signaux; Nouveau   les applications doivent utiliser sigaction ()   plutôt que signal ()

http://pubs.opengroup.org/onlinepubs/009695399/ functions / signal.html # tag_03_690_07

Que vous partiez de zéro ou que vous modifiiez un ancien programme, sigaction devrait être la bonne option.

signal () est le standard C, sigaction () ne l’est pas.

Si vous pouvez utiliser l’un ou l’autre (c’est-à-dire que vous êtes sur un système POSIX), utilisez sigaction (); il n'est pas précisé si signal () réinitialise le gestionnaire, ce qui signifie que pour être portable, vous devez appeler à nouveau signal () à l'intérieur du gestionnaire. Le pire, c'est qu'il y a une course: si vous recevez deux signaux en succession rapide et que le second est délivré avant de réinstaller le gestionnaire, vous obtiendrez l'action par défaut, qui va probablement tuer votre processus. sigaction () , en revanche, est assuré d'utiliser & # 8220; fiable & # 8221; sémantique du signal. Vous n'avez pas besoin de réinstaller le gestionnaire, car il ne sera jamais réinitialisé. Avec SA_RESTART, vous pouvez également faire redémarrer automatiquement certains appels système (vous n'avez donc pas à vérifier manuellement EINTR). sigaction () a plus d'options et est fiable. Son utilisation est donc encouragée.

Psst ... ne dites rien à personne de ce que je vous ai dit, mais POSIX a actuellement une fonction bsd_signal () qui agit comme signal () mais donne la sémantique BSD, ce qui signifie qu'elle est fiable. Son utilisation principale est le portage d’anciennes applications utilisant des signaux fiables, et POSIX déconseille de l’utiliser.

À partir de la page de manuel signal (3) :

  

DESCRIPTION

 This signal() facility is a simplified interface to the more
 general sigaction(2) facility.

Les deux invoquent la même installation sous-jacente. Vous ne devez probablement pas manipuler la réponse d’un seul signal avec les deux, mais leur mélange ne devrait causer aucune cassure ...

J'utiliserais signal () car il est plus portable, du moins en théorie. Je vais mettre n'importe quel commentateur capable de créer un système moderne ne disposant pas de couche de compatibilité POSIX et prenant en charge signal ().

Citation tirée de la documentation GLIBC :

  

Il est possible d'utiliser à la fois les fonctions signal et sigaction dans   un seul programme, mais vous devez faire attention car ils peuvent   interagir de manière légèrement étrange.

     

La fonction sigaction spécifie plus d'informations que le signal   fonction, donc la valeur de retour du signal ne peut pas exprimer la totalité   gamme de possibilités de sigaction. Par conséquent, si vous utilisez signal pour   sauvegarder et rétablir plus tard une action, il peut ne pas être en mesure de   rétablir correctement un gestionnaire établi avec sigaction.

     

Pour éviter les problèmes, utilisez toujours sigaction pour enregistrer   et restaurez un gestionnaire si votre programme utilise la sigaction. Puisque   la sigaction est plus générale, elle peut correctement sauvegarder et rétablir   action, qu'elle ait été établie ou non à l'origine   signal ou sigaction.

     

Sur certains systèmes, si vous établissez une action avec signal puis   examinez-le avec sigaction, l'adresse du gestionnaire que vous obtenez ne peut pas   être identique à ce que vous avez spécifié avec signal. Il se peut même pas   convient pour être utilisé comme argument d'action avec signal. Mais vous pouvez compter   sur l'utiliser comme un argument de sigaction. Ce problème n'arrive jamais   sur le système GNU.

     

Donc, vous feriez mieux d'utiliser l'un ou l'autre des mécanismes   toujours au sein d'un même programme.

     

Remarque sur la portabilité: la fonction de signal de base est une fonctionnalité de la norme ISO C,   tandis que sigaction fait partie de la norme POSIX.1. Si vous êtes   préoccupé par la portabilité vers des systèmes non-POSIX, alors vous devriez   utilisez plutôt la fonction signal.

     
    

Droit d'auteur (C) 1996-2008 de la Free Software Foundation, Inc.

         

L’autorisation est donnée de copier, distribuer et / ou modifier cette     document sous les termes de la licence de documentation libre GNU,     Version 1.2 ou toute version ultérieure publiée par le logiciel libre     Fondation; sans sections invariantes, sans textes de couverture,     et sans texte de couverture arrière. Une copie de la licence est incluse dans     la section intitulée "Licence de documentation libre GNU".

  

Je suggérerais également d’utiliser sigaction () sur signal () et voudrais ajouter un point de plus. sigaction () vous donne plus d'options comme le pid du processus qui est mort (possible en utilisant la structure siginfo_t).

De la page de manuel signal (7)

  

Un signal dirigé par le processus peut être délivré à tout     l'un des threads dont le signal n'est pas bloqué actuellement.     Si le signal est débloqué dans plusieurs des threads, alors le     Le noyau choisit un thread arbitraire auquel délivrer le signal.

Et je dirais que ce "problème" existe pour signal (2) et sigaction (2) . Soyez donc prudent avec les signaux et les pthreads.

... et signal (2) semble appeler sigaction (2) sous Linux avec la glibc.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top