Je décrirai l'historique jusqu'à ce que je gère le signal d'abandon en tant que débutant en langage C (niveau de formation des nouveaux arrivants).
Je vous serais reconnaissant de bien vouloir souligner les points de la description ou du code.
Le projet utilise Redhat Enterprise Linux 7, mais cette fois, il sera décrit dans Cent OS 7.
Les signaux font partie des communications interprocessus utilisées sous UNIX et Linux. C'est difficile à imaginer quand on l'entend en un coup d'œil, mais il est utilisé lorsque l'on souhaite arrêter un programme ou une commande sur le terminal ou lorsque l'on souhaite générer un événement spécifique. Cela s'applique également à la tentative d'arrêt d'une commande ou d'un programme en cours d'exécution avec ** Ctrl + C **.
Il existe deux types de signaux traités par Linux: ** les signaux standard ** et ** les signaux en temps réel **. De plus, il y a 32 signaux pour chacun, ce qui signifie qu'il y a un total de 64 signaux. A ce moment, les signaux standard reçoivent les numéros 1 à 32 et les signaux en temps réel sont affectés aux numéros 33 à 64. Je pense que les signaux que vous entendez souvent à propos de Linux sont les signaux standards ** SIGINT ** et ** SIGKILL **.
Pour plus de détails sur les signaux, reportez-vous à la page de manuel de SIGNAL.
Cette fois, je voudrais décrire basé sur ~~ SIGABRT ~~ SIGINT.
Pour plus d'informations sur ~~ SIGABRT, veuillez vous référer à Man page of ABORT. ~~
Il existe deux façons de capturer des signaux en langage C: la fonction signal
et la fonction sigaction
. Tout d'abord, je voudrais confirmer avec signal
.
Le code réellement écrit est le suivant.
test_signal.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
volatile sig_atomic_t e_flag = 0;
void abrt_handler(int sig);
int main() {
printf("start %s\n", __func__);
if ( signal(SIGINT, abrt_handler) == SIG_ERR ) {
exit(1);
}
while (!e_flag) {}
printf("end %s\n", __func__);
return 0;
}
void abrt_handler(int sig) {
e_flag = 1;
}
Résultat d'exécution
[root@ca035c198d1f work]# ./signal_test.o
start main
^Chandle signal : 2
end main
Ce qui suit est le code NG avant de recevoir votre indication. Je laisserai la description comme un échantillon NG.
test_signal.c(NG)
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
jmp_buf buf;
void abrt_handler(int sig);
int main() {
printf("start %s\n", __func__);
// SIG_Réglez la poignée de ABRT.
//Si la poignée échoue, le processus se termine.
if ( signal(SIGABRT, abrt_handler) == SIG_ERR ) {
exit(1);
}
//Définissez le point de retour après la poignée de signal.
//Empêchez que l'annulation soit rappelée après le retour
if ( setjmp(buf) == 0 ) {
printf("publish abort\n");
abort();
}
printf("end %s\n", __func__);
return 0;
}
//Fonction de poignée de signal
void abrt_handler(int sig) {
printf("handle signal : %d\n", sig);
//Après avoir affiché le message, il revient à setjmp.
longjmp(buf, 1);
}
Le résultat de l'exécution est le suivant.
Résultat d'exécution
[root@ca035c198d1f work]# ./signal_test.o
start main
publish abort
handle signal : 6
end main
Comme vous pouvez le voir dans le code, ce n'est pas si difficile.
Définissez le signal que vous souhaitez gérer dans la fonction signal
et spécifiez la fonction dans le deuxième argument. De cette façon, ʻabrt_handler sera exécuté lorsque SIGABRT se produit. De plus, le numéro du signal généré est entré dans le premier argument lorsque ʻabrt_handler
est appelé. Cette fois, "6" est entré.
~~ Puisque nous émettons un abandon cette fois, si vous revenez au même endroit après avoir traité le signal, le processus se terminera tel quel. Par conséquent, nous utilisons longjmp
pour empêcher le processus de se terminer en ne passant pas par le même endroit. ~~
Depuis que vous avez signalé le fonctionnement de longjmp
dans le gestionnaire de signaux, nous l'avons corrigé.
Concernant longjmp
dans le gestionnaire de signaux, il y a la description suivante dans JPCERT CC.
L'appel de la fonction longjmp () depuis un gestionnaire de signaux peut entraîner un comportement indéfini et compromettre l'intégrité du programme. Par conséquent, ni longjmp () ni POSIX siglongjmp () ne doivent être appelés depuis le gestionnaire de signaux.
J'ai trouvé que le code ci-dessus peut gérer les signaux, mais les poignées de signal utilisant la fonction singal
ne sont pas recommandées.
Comme mentionné dans la page Man de SIGNAL, il est dit que vous devriez éviter le «signal» moins portable et utiliser «sigaction». Les détails peuvent être trouvés sur Page de manuel de SIGNAL.
Maintenant, essayons l'implémentation avec sigaction
.
Le code réellement écrit est le suivant.
sigaction_test.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
void abrt_handler(int sig, siginfo_t *info, void *ctx);
volatile sig_atomic_t eflag = 0;
int main() {
printf("start %s\n", __func__);
struct sigaction sa_sigabrt;
memset(&sa_sigabrt, 0, sizeof(sa_sigabrt));
sa_sigabrt.sa_sigaction = abrt_handler;
sa_sigabrt.sa_flags = SA_SIGINFO;
if ( sigaction(SIGINT, &sa_sigabrt, NULL) < 0 ) {
exit(1);
}
while ( !eflag ) {}
printf("end %s\n", __func__);
return 0;
}
void abrt_handler(int sig, siginfo_t *info, void *ctx) {
// siginfo_Il est affiché par printf pour vérifier si la valeur de t a été acquise.
//À l'origine, printf n'est pas sécurisé de manière asynchrone et ne doit pas être utilisé ici.
printf("si_signo:%d\nsi_code:%d\n", info->si_signo, info->si_code);
printf("si_pid:%d\nsi_uid:%d\n", (int)info->si_pid, (int)info->si_uid);
eflag = 1;
}
Le résultat de l'exécution est le suivant.
Résultat d'exécution
[root@ca035c198d1f work]# ./sigaction_test.o
start main
^Csi_signo:2
si_code:128
si_pid:0
si_uid:0
end main
Ce qui suit est le code NG avant de recevoir votre indication. Je laisserai la description comme un échantillon NG.
sigaction_test.c(NG)
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
#include <unistd.h>
#include <string.h>
jmp_buf buf;
void abrt_handler(int sig, siginfo_t *info, void *ctx);
int main() {
printf("start %s\n", __func__);
//Paramètres de sigaction
struct sigaction sa_sigabrt;
memset(&sa_sigabrt, 0, sizeof(sa_sigabrt));
sa_sigabrt.sa_sigaction = abrt_handler;
sa_sigabrt.sa_flags = SA_SIGINFO;
// SIG_Réglez la poignée de ABRT.
//Si la poignée échoue, le processus se termine.
if ( sigaction(SIGABRT, &sa_sigabrt, NULL) < 0 ) {
exit(1);
}
//Définissez le point de retour après la poignée de signal.
//Empêchez que l'annulation soit rappelée après le retour
if ( setjmp(buf) == 0 ) {
printf("publish abort\n");
abort();
}
printf("end %s\n", __func__);
return 0;
}
void abrt_handler(int sig, siginfo_t *info, void *ctx) {
printf("si_signo:%d\nsi_code:%d\n", info->si_signo, info->si_code);
printf("si_pid:%d\nsi_uid:%d\n", (int)info->si_pid, (int)info->si_uid);
//Après avoir affiché le message, il revient à setjmp.
longjmp(buf, 1);
}
Le résultat de l'exécution est le suivant.
Résultat d'exécution
[root@ca035c198d1f work]# start main
publish abort
si_signo:6
si_code:-6
si_pid:79
si_uid:0
end main
^C
[1]+ Done ./sigaction_test.o
Comme vous pouvez le voir dans le code, il est défini plus souvent que la fonction signal
. Pour sigaction
, la fonction à exécuter au moment de la manipulation et sa_flags
sont définis. Les paramètres de la fonction sont à peu près les mêmes que «signal», mais «sa_flags» peut être défini sur différentes valeurs. Cette fois, j'ai mis SA_SIGINFO
pour que je puisse obtenir les informations lorsque le signal a été envoyé.
Je pense que l'argument de la fonction à exécuter au moment du traitement du signal est différent de celui au moment du "signal". Cette fois, puisque SA_SIGINFO
est défini dans sa_flags
, siginfo_t
où les informations sont stockées devient l'argument. Au moment de la manipulation, vous pouvez voir que l'ID de processus au moment de l'exécution peut être obtenu à partir de * info
of siginfo_t
.
Il y a aussi «ctx», mais cette fois je ne l'ai pas étudié jusqu'à présent.
C'était difficile, mais maintenant je peux gérer le signal. Cette fois, nous ne le faisons que pour l'abandon, mais nous pouvons faire de même pour d'autres signaux.
En ce qui concerne le contenu décrit cette fois, je ne suis honnêtement pas confiant sur les manières de signaler, donc si vous avez des suggestions ou des conseils tels que j'écris ceci, veuillez me le faire savoir.
Recommended Posts