Utilisation sigaction()
sauf si vous avez des raisons très convaincantes de ne pas le faire.
L' signal()
interface a l'antiquité (et donc la disponibilité) en sa faveur, et elle est définie dans le standard C. Néanmoins, il a un certain nombre de caractéristiques indésirables qui sigaction()
évitent - à moins que vous n'utilisiez les indicateurs explicitement ajoutés pour sigaction()
lui permettre de simuler fidèlement l'ancien signal()
comportement.
- La
signal()
fonction 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.
- La
signal()
fonction réinitialise (généralement) l'action du signal à SIG_DFL
(par défaut) pour presque tous les signaux. Cela signifie que lesignal()
gestionnaire doit se réinstaller 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é pendant lequel si une deuxième instance du signal arrive, le comportement par défaut (généralement se termine, parfois avec préjudice - aka core dump) se produit.
- Le comportement exact de
signal()
varie selon les systèmes - et les normes permettent 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 les deux que vous utilisez, ne soyez pas tentés par les interfaces de signal alternatives telles que
sighold()
,
sigignore()
,
sigpause()
et
sigrelse()
. Ils sont nominalement des alternatives à sigaction()
, mais ils sont à peine standardisés et sont présents dans POSIX pour une compatibilité descendante plutôt que pour une utilisation sérieuse. Notez que le standard POSIX dit que leur comportement dans les programmes multi-threads n'est pas défini.
Les programmes et signaux multithreads sont une toute autre histoire compliquée. AFAIK, les deux signal()
et sigaction()
sont OK dans les applications multi-thread.
Cornstalks observe :
La page de manuel Linux pour signal()
dit:
Les effets de signal()
dans un processus multithread ne sont pas spécifiés.
Ainsi, je pense que sigaction()
c'est le seul qui peut être utilisé en toute sécurité dans un processus multi-thread.
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 si le processus est monothread et qu'un gestionnaire de signal est exécuté autrement que par:
- L'appel de processus
abort()
, raise()
, kill()
, pthread_kill()
ou sigqueue()
pour générer un signal qui est pas bloqué
- Un signal en attente étant débloqué et délivré avant l'appel qui l'a débloqué, il revient
le comportement est indéfini si le gestionnaire de signal fait référence à un objet autre que celui errno
avec une durée de stockage statique autrement qu'en affectant une valeur à un objet déclaré comme volatile sig_atomic_t
, ou si le gestionnaire de signal appelle une fonction définie dans cette norme autre que l'une des fonctions répertoriées dans Concepts de signal .
Ainsi POSIX spécifie clairement le comportement de signal()
dans une application multi-thread.
Néanmoins, il sigaction()
doit être préféré dans pratiquement toutes les circonstances - et le code multi-thread portable devrait être utilisé à sigaction()
moins qu'il n'y ait une raison écrasante pour laquelle il ne le peut pas (comme "n'utiliser que les fonctions définies par le Standard C" - et oui, le code C11 peut être multi -fileté). C'est essentiellement ce que dit également le premier paragraphe de cette réponse.
signal
est en fait le comportement d'Unix System V. POSIX autorise ce comportement ou le comportement BSD beaucoup plus sain, mais comme vous ne pouvez pas être sûr de celui que vous obtiendrez, il est toujours préférable de l'utilisersigaction
.SA_RESETHAND
, aussiSA_NODEFER
.sigaction()
, vous êtes essentiellement obligé d'utiliser la spécification Standard C poursignal()
. Cependant, cela vous donne un ensemble d'options extrêmement pauvres pour ce que vous pouvez faire. Vous pouvez: modifier (portée du fichier) des variables de typevolatile sig_atomic_t
; appeler l'une des fonctions de «sortie rapide» (_Exit()
,quick_exit()
) ouabort()
; appelsignal()
avec le numéro de signal actuel comme argument de signal; revenir. Et c'est tout. Rien d'autre n'est garanti pour être portable. C'est tellement strict que la plupart des gens ignorent ces règles - mais le code qui en résulte est douteux.sigaction()
démo de GCC eux-mêmes: gnu.org/software/libc/manual/html_node / ... ; et une excellentesignal()
démo de GCC eux-mêmes: gnu.org/software/libc/manual/html_node/… . Notez que dans lasignal
démo, ils évitent de changer le gestionnaire d'ignorer (SIG_IGN
) si c'est ce à quoi il était auparavant intentionnellement défini.Pour moi, cette ligne ci-dessous était suffisante pour décider:
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.
la source
Ce sont des interfaces différentes pour les installations de signal du système d'exploitation. On devrait préférer utiliser sigaction pour signaler si possible car signal () a un comportement défini par l'implémentation (souvent propice à la course) et se comporte différemment sur Windows, OS X, Linux et autres systèmes UNIX.
Consultez cette note de sécurité pour plus de détails.
la source
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 spécifié si signal () réinitialise le gestionnaire, ce qui signifie que pour être portable, vous devez à nouveau appeler signal () dans le gestionnaire. Ce qui est pire, c'est qu'il y a une course: si vous obtenez deux signaux en succession rapide et que le second est livré avant de réinstaller le gestionnaire, vous aurez l'action par défaut, qui sera probablement de tuer votre processus. sigaction () , d'autre part, est garanti d'utiliser une sémantique de signal «fiable». 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, donc son utilisation est encouragée.
Psst ... ne dites à personne que je vous ai dit cela, mais POSIX a actuellement une fonction bsd_signal () qui agit comme signal () mais donne une sémantique BSD, ce qui signifie qu'elle est fiable. Son utilisation principale est le portage d'anciennes applications qui supposaient des signaux fiables, et POSIX ne recommande pas de l'utiliser.
la source
bsd_signal()
- certaines implémentations POSIX peuvent avoir la fonction, mais POSIX lui-même ne contient pas une telle fonction (voir POSIX ).En bref:
sigaction()
est bon et bien défini, mais est une fonction Linux et ne fonctionne donc que sous Linux.signal()
est mauvaise et mal définie, mais est une fonction standard C et donc elle fonctionne sur n'importe quoi.Que disent les pages de manuel Linux à ce sujet?
man 2 signal
(voir en ligne ici ) déclare:En d'autres termes: ne pas utiliser
signal()
. Utilisezsigaction()
plutôt!Que pense GCC?
Source: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
Donc, si Linux et GCC disent de ne pas utiliser
signal()
, mais d'utiliser à lasigaction()
place, cela soulève la question: comment diable utilisons-nous cettesigaction()
chose déroutante !?Exemples d'utilisation:
Lisez EXCELLENT
signal()
exemple de GCC ici: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-HandlingEt leur EXCELLENT
sigaction()
exemple ici: https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.htmlAprès avoir lu ces pages, j'ai mis au point la technique suivante pour
sigaction()
:1.
sigaction()
, car c'est la bonne façon d'attacher un gestionnaire de signaux, comme décrit ci-dessus:2. Et pour
signal()
, même si ce n'est pas un bon moyen d'attacher un gestionnaire de signaux, comme décrit ci-dessus, il est toujours bon de savoir comment l'utiliser.Voici le code de démonstration GCC copié, car il est à peu près aussi bon qu'il va l'être:
Les principaux liens à connaître:
signal()
exemple d'utilisation officiel de GCC : https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handlingsigaction()
Exemple d'utilisation officiel de GCC : https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.htmlsigemptyset()
etsigfillset()
; Je ne les comprends toujours pas exactement, mais je sais qu'ils sont importants: https://www.gnu.org/software/libc/manual/html_node/Signal-Sets.htmlVoir également:
la source
Depuis la
signal(3)
page de manuel:Les deux invoquent la même fonction sous-jacente. Vous ne devriez probablement pas manipuler la réponse d'un seul signal avec les deux, mais les mélanger ne devrait pas faire casser quoi que ce soit ...
la source
Je suggérerais également d'utiliser sigaction () sur signal () et je voudrais ajouter un point de plus. sigaction () vous donne plus d'options telles que pid du processus qui est mort (possible en utilisant la structure siginfo_t).
la source
J'utiliserais signal () car il est plus portable, du moins en théorie. Je voterai pour n'importe quel commentateur qui peut proposer un système moderne qui n'a pas de couche de compatibilité POSIX et prend en charge signal ().
Citant la documentation GLIBC :
la source
Depuis le signal de la page de manuel (7)
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.
la source