web-dev-qa-db-fra.com

Quelle est la difference entre sigaction et signal?

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 j’écrivais de toutes pièces, lequel devrais-je choisir?

118
MattSmith

Utilisez sigaction() _ sauf si 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 elle est définie dans la norme C. Néanmoins, sigaction() évite un certain nombre de caractéristiques indésirables, à moins que vous n'utilisiez les indicateurs explicitement ajoutés à sigaction() pour lui permettre de simuler fidèlement l'ancien comportement signal().

  1. La fonction signal() ne bloque pas (nécessairement) l’arrivée d’autres signaux pendant l’exécution du gestionnaire actuel; 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 se réinstaller d’abord. 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 préjudice - core dump) se produit.
  3. Le comportement exact de signal() varie selon les systèmes - et les normes autorisent ces variations.

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

Quel que soit le type utilisé, 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 les signaux multithreads sont une toute autre histoire compliquée. Autant que je sache, les fonctions signal() et sigaction() sont acceptables dans les applications multithreads.

Cornstalks _ { observe) :

La page de manuel Linux pour signal() indique:

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

Ainsi, je pense que sigaction() est le seul qui puisse ê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 qui n'est pas bloqué
  • Un signal en attente étant débloqué et remis avant l'appel qui l'a débloqué, il le renvoie

le comportement est indéfini si le gestionnaire de signaux 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é comme volatile sig_atomic_t ou si le gestionnaire de signaux appelle une fonction définie dans la présente norme autre qu'une des fonctions énumérés 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 pratiquement toutes les circonstances - et le code multi-thread portable doit utiliser sigaction() sauf en cas de raison impérieuse (par exemple, "utiliser uniquement les fonctions définies par la norme C" - et oui, le code C11 peut être multi-threaded). Ce qui est essentiellement ce que dit le paragraphe d’ouverture de cette réponse.

146
Jonathan Leffler

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

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

5
ididak

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.

4
San

signal () est le standard C, pas sigaction ().

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 signal () à nouveau à l'intérieur du gestionnaire. Ce qui est pire, c’est qu’il ya une course: si vous recevez deux signaux en succession rapide et que le second est envoyé avant de réinstaller le gestionnaire, vous aurez l’action par défaut, qui va probablement tuer votre processus . sigaction () , d’autre part, garantit l’utilisation de la sémantique «fiable» 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 le dis à personne de ce que je t'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 les utiliser.

3
Huzzefakhan

De lasignal(3)man page:

LA 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 devriez probablement pas manipuler la réponse d'un seul signal avec les deux, mais les mélanger ne devrait causer aucune cassure ...

2
dmckee

Je suggérerais également d’utiliser sigaction () plutôt que signal () et voudrais ajouter un point supplémentaire.

1

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

Citant la documentation GLIBC :

Il est possible d'utiliser à la fois les fonctions signal et sigaction dans un seul programme, mais vous devez être prudent, 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 le maximum gamme de possibilités de sigaction. Par conséquent, si vous utilisez le signal to sauvegarder et rétablir plus tard une action, il ne pourra peut-être pas rétablir correctement un gestionnaire établi avec sigaction.

Pour éviter tout problème, utilisez toujours sigaction pour enregistrer et restaurez un gestionnaire si votre programme utilise la sigaction. Puisque La signature est plus générale, elle peut correctement sauvegarder et rétablir n'importe quel fichier 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 peut ne pas être identique à ce que vous avez spécifié avec signal. Ce n'est peut-être 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 ne se produit jamais sur le système GNU.

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

Portabilité Remarque: La fonction de signal de base est une fonctionnalité de 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, vous devriez alors utilisez plutôt la fonction signal.

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

La permission est donnée de copier, distribuer et/ou modifier ceci 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".

1
bmdhacks

De la page de manuel signal (7)

Un signal dirigé par le processus peut être délivré à n’importe quel l'un des threads dont le signal n'est pas bloqué actuellement . Si le signal est débloqué dans plusieurs threads, le fichier 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 pistes de lecture.

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

0
robert.berger