[LINUX] Gérer les signaux en langage C

introduction

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.

environnement

Le projet utilise Redhat Enterprise Linux 7, mais cette fois, il sera décrit dans Cent OS 7.

À propos des signaux

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. ~~

En fait gérer l'abandon

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. ~~

Postscript

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.

Résumé

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

Gérer les signaux en langage C
Accéder à MongoDB en C
Next Python en langage C
Gérer le démarquage avec python
API C en Python 3
Étendre python en C ++ (Boost.NumPy)
Intégration du langage machine en langage C
Gérer les constantes dans les modèles Django
Tri de tas fait en langage C
Utiliser des expressions régulières en C
Gérer les variables d'environnement en Python
Imiter Numpy de Python en C #
Recherche binaire en Python / C ++
Arborescence de surface totale minimale en C #
Gérer les nombres complexes en Python
Détectez et traitez les signaux en Java.
Comment gérer une session dans SQLAlchemy
Ecrire un test piloté par table en C
Test de module multi-instance en langage C
Réaliser une classe d'interface en langage C
ABC166 en Python A ~ C problème
Gérer les dates dans les commandes bash Linux
Gérer les files d'attente de messages posix en python
Gérez les données au format NetCDF avec Python
Gérez le format GDS II avec Python
Gérer les demandes dans un processus distinct
Lors de la lecture d'une structure C ++ avec Cython
Résoudre ABC036 A ~ C avec Python
Comment envelopper C en Python
Résoudre ABC037 A ~ C avec Python
Segfo avec 16 caractères en langage C
Ecrire un test unitaire de langage C en Python
Comment gérer le japonais avec Python
Gérez plusieurs versions de python en un seul jupyter
Liste de liens (list_head / queue) en langage C
Résoudre ABC175 A, B, C avec Python
Algorithme en Python (ABC 146 C Dichotomy
Implémenter une partie du processus en C ++
Implémenter le filtre FIR en langage Python et C
Comment utiliser Google Test en langage C
Exemple de script pour piéger les signaux en Python
Ecrire le fichier O_SYNC en C et Python
Tri sélect écrit en C
Comment gérer des valeurs consécutives dans MySQL
Générer un langage C à partir d'une expression S avec Python
Utilisez libxlsxwriter pour exporter des fichiers xlsx en C ++.
Exécutez Python en C ++ sur Visual Studio 2017
Communiquez avec les périphériques I2C sous Linux C