Quelle est la difference entre sigaction et signal?
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?
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.
- 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. - La fonction
signal ()
(généralement) réinitialise l'action du signal surSIG_DFL
(valeur par défaut) pour presque tous les signaux. Cela signifie que le gestionnairesignal ()
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> - 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.
La page de manuel Linux de
signal ()
dit:
& nbsp; & nbsp;
Les effets designal ()
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 ()
, ousigqueue ()
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.