Comme j'étais accro à la mise en œuvre de signal (), je laisserai les résultats de l'enquête sous forme de mémorandum. L'environnement d'enquête est
Je refactorisais le code hérité qui a été créé autour de l'année de ma naissance et modifié et développé pour correspondre à l'évolution des plates-formes d'exploitation.
Certains d'entre eux ont utilisé l'appel système signal (). En regardant man de signal (), évitez d'utiliser signal () car son comportement change en fonction de la plateforme. , Sigaction () a été utilisé, donc je l'ai modifié pour utiliser sigaction (), mais il tient ici.
SIGNAL(2) Linux Programmer's Manual
Nom
signal -Manipulation du signal ANSI C
La description
signal()Le comportement dépend de la version d'UNIX.
Historiquement, cela dépend aussi de la version de Linux.
Évitez d'utiliser cet appel système et à la place sigaction(2)utiliser.
………
En conclusion, Linux signal () se comporte différemment selon les options de compilation. Même si vous appelez signal (), il est enveloppé dans la glibc, et en interne, il appelle l'appel système sigaction (). Lors de l'appel de cet appel système sigaction (), la valeur de sa_flags, qui est un argument, diffère selon l'option de compilation.
Examinons d'abord l'implémentation interne du noyau Linux.
/*
* For backwards compatibility. Functionality superseded by sigaction.
*/
SYSCALL_DEFINE2(signal, int, sig, __sighandler_t, handler)
{
struct k_sigaction new_sa, old_sa;
int ret;
new_sa.sa.sa_handler = handler;
new_sa.sa.sa_flags = SA_ONESHOT | SA_NOMASK;
sigemptyset(&new_sa.sa.sa_mask);
ret = do_sigaction(sig, &new_sa, &old_sa);
return ret ? ret : (unsigned long)old_sa.sa.sa_handler;
}
Il convient de noter que l'indicateur spécifié dans sa_flags est (SA_ONESHOT | SA_NOMASK). SA_ONESHOT signifie que sa_handler retourne à SIG_DFL lorsque le processus reçoit un signal. SA_NOMASK signifie que lorsqu'un processus reçoit un signal et exécute un gestionnaire, il reçoit à nouveau le même signal (c'est-à-dire qu'il ne le masque pas). C'est le comportement du signal du System V (). (Unix a deux systèmes majeurs, System V et BSD, et il semble que l'implémentation soit différente à certains endroits. Je ne sais pas en détail car j'ai été la première personne à toucher Linux (OS autre que Windows) en 2012. )
Par conséquent, si vous utilisez le noyau Linux ver.5.5.5, vous pouvez penser que la spécification de signal () est l'implémentation de System V, mais comme mentionné ci-dessus, lorsque vous utilisez la glibc, signal () dans le noyau est Pas appelé.
Lors de l'appel d'un appel système à partir du langage C, il s'agit essentiellement d'une forme d'appel d'une fonction wrapper de la glibc. Jetez un œil à signal.h (/usr/include/signal.h), qui est inclus lors de l'appel de signal ().
/usr/include/signal.h
/* Set the handler for the signal SIG to HANDLER, returning the old
handler, or SIG_ERR on error.
By default `signal' has the BSD semantic. */
#ifdef __USE_MISC
extern __sighandler_t signal (int __sig, __sighandler_t __handler)
__THROW;
#else
/* Make sure the used `signal' implementation is the SVID version. */
# ifdef __REDIRECT_NTH
extern __sighandler_t __REDIRECT_NTH (signal,
(int __sig, __sighandler_t __handler),
__sysv_signal);
# else
# define signal __sysv_signal
# endif
#endif
Il diffère des trois manières suivantes.
Je vais expliquer chacun.
Par défaut, \ _ \ _USE \ _MISC est défini. Vous pouvez le voir en regardant /usr/include/features.h. L'explication de ici était facile à comprendre.
/usr/include/signal.h
extern __sighandler_t signal (int __sig, __sighandler_t __handler)
__THROW;
signal () est déclaré extern et l'entité est dans la glibc. La mise en œuvre est la suivante.
c:glibc-2.28/sysdeps/posix/signal.c
sigset_t _sigintr attribute_hidden; /* Set by siginterrupt. */
__sighandler_t
__bsd_signal (int sig, __sighandler_t handler)
{
struct sigaction act, oact;
<Omis>
act.sa_handler = handler;
__sigemptyset (&act.sa_mask);
__sigaddset (&act.sa_mask, sig);
act.sa_flags = __sigismember (&_sigintr, sig) ? 0 : SA_RESTART;
if (__sigaction (sig, &act, &oact) < 0)
return SIG_ERR;
return oact.sa_handler;
}
weak_alias (__bsd_signal, signal)
Il s'agit d'une alias faible (\ _ \ _ bsd \ _signal, signal), et lorsque signal () est appelé, \ _ \ _ bsd \ _signal () est appelé. C'est un comportement BSD, comme vous pouvez le voir à partir du nom \ _ \ _ bsd \ _signal (). Ceci est cohérent avec la description "Par défaut" signal "a la sémantique BSD." Dans le commentaire sur la première ligne de /usr/include/signal.h.
Ce à quoi nous prêtons attention cette fois, c'est la valeur spécifiée pour act.sa_flags. Je pense que \ _ \ _ sigisempty () renverra 0 (je me demande s'il renverra autre chose que 0), donc act.sa_flags est toujours SA_RESTART. Dans SA_RESTART, le gestionnaire est automatiquement réenregistré une fois que le processus a reçu le signal et a terminé le traitement du gestionnaire. Si un signal est reçu pendant l'exécution de l'appel système, le traitement continuera, et s'il était dans l'état d'attente, il sera à nouveau dans l'état d'attente, et aucune erreur ne sera renvoyée par EINTR. Par exception, dans les appels système de la famille de sockets et de la famille msg, lorsqu'un signal est reçu, une erreur est renvoyée par EINTR.
Avant d'expliquer l'implémentation de signal (), je présenterai le cas où \ _ \ _USE \ _MISC n'est pas défini.
Par exemple, l'ajout de l'option -ansi rend \ _ \ _USE \ _MISC indéfini. Vous pouvez le vérifier avec la commande suivante.
$ echo "#include <signal.h>" | gcc -ansi -dM -E - | grep __USE_
#define __USE_FORTIFY_LEVEL 0
#define __bos(ptr) __builtin_object_size (ptr, __USE_FORTIFY_LEVEL > 1)
À propos, __USE_MISC n'est pas défini même si -D_XOPEN_SOURCE est spécifié comme option.
$ echo "#include <signal.h>" | gcc -D_XOPEN_SOURCE -dM -E - | grep __USE_
#define __USE_FORTIFY_LEVEL 0
#define __USE_ISOC11 1
#define __USE_ISOC95 1
#define __USE_ISOC99 1
#define __USE_XOPEN 1
#define __USE_POSIX2 1
#define __USE_POSIX 1
#define __bos(ptr) __builtin_object_size (ptr, __USE_FORTIFY_LEVEL > 1)
#define __USE_POSIX_IMPLICITLY 1
En revanche, si -ansi ou -D_XOPEN_SOURCE n'est pas spécifié comme option comme indiqué ci-dessous, c'est-à-dire que par défaut, __USE_MISC est défini.
$ echo "#include <features.h>" | gcc -dM -E - | grep __USE_
#define __USE_FORTIFY_LEVEL 0
#define __USE_ISOC11 1
#define __USE_ISOC95 1
#define __USE_ISOC99 1
#define __USE_XOPEN2K 1
#define __USE_POSIX199506 1
#define __USE_POSIX2 1
#define __USE_XOPEN2K8 1
#define __USE_MISC 1
#define __USE_POSIX 1
#define __bos(ptr) __builtin_object_size (ptr, __USE_FORTIFY_LEVEL > 1)
#define __USE_POSIX199309 1
#define __USE_POSIX_IMPLICITLY 1
#define __USE_ATFILE 1
Notez que \ _ \ _REDIRECT \ _NTH est défini indépendamment de la présence ou de l'absence de -ansi ou -D_XOPEN_SOURCE.
La déclaration de signal () lorsque \ _ \ _USE \ _MISC n'est pas défini et que \ _ \ _ REDIRECT \ _NTH est défini est la suivante.
/usr/include/signal.h
extern __sighandler_t __REDIRECT_NTH (signal,
(int __sig, __sighandler_t __handler),
__sysv_signal);
\ _ \ _REDIRECT \ _NTH est une macro qui indique au compilateur d'utiliser le symbole __sysv_signal au lieu du signal de symbole Cela semble être -nth-do-in-unistd-h), donc lorsque vous appelez signal (), __sysv_signal () de la glibc est appelée.
L'implémentation de __sysv_signal () est la suivante.
sysdeps/posix/sysv_signal.c
/* Set the handler for the signal SIG to HANDLER,
returning the old handler, or SIG_ERR on error. */
__sighandler_t
__sysv_signal (int sig, __sighandler_t handler)
{
struct sigaction act, oact;
<Omis>
act.sa_handler = handler;
__sigemptyset (&act.sa_mask);
act.sa_flags = SA_ONESHOT | SA_NOMASK | SA_INTERRUPT;
act.sa_flags &= ~SA_RESTART;
if (__sigaction (sig, &act, &oact) < 0)
return SIG_ERR;
return oact.sa_handler;
}
weak_alias (__sysv_signal, sysv_signal)
Regardez la valeur de sa_flags à chaque fois. SA_ONESHOT et SA_NOMASK sont les mêmes que signal () à l'intérieur du noyau, mais sont également marqués comme SA_INTERRUPT. Ce SA_INTERRUPT n'est pas expliqué dans l'homme de sigaction, et quand je regarde la glibc, il dit / * No-op historique. * /, Alors puis-je l'ignorer? Je bat poliment le bit SA_RESTART, mais je ne suis pas sûr. Existe-t-il un environnement qui a les mêmes bits que SA_INTERRUPT? En premier lieu, SA_RESTART est un drapeau unique à l'extension BSD, je me demande donc s'il est soigneusement vaincu car il n'existe pas dans l'implémentation du système v.
Puisqu'il est déclaré dans /usr/include/signal.h comme suit, on peut voir que __sysv_signal () est appelé lorsque signal () est appelé indépendamment de la présence ou de l'absence de \ _ \ _REDIRECT \ _NTH. L'utilisation de \ _ \ _REDIRECT \ _NTH affecte-t-elle quelque chose? Je n'étais pas sûr.
/usr/include/signal.h
# define signal __sysv_signal
Lorsque j'examinais le code source de la glibc, la fonction portant le même nom était implémentée à plusieurs endroits, et je n'étais pas sûr de celle qui était appelée. Pour le moment, je regardais le code source suivant en tenant compte de mon propre environnement.
Puisque le comportement de signal () change en fonction de l'option de compilation, le comportement a changé lorsque toutes les modifications de signal () -> sigaction () ont été modifiées de la même manière avec la mort cérébrale.
Nous espérons que cet article aidera ceux qui maintiennent et refactorisent le code hérité (et bien sûr d'autres).
Recommended Posts