web-dev-qa-db-fra.com

Fournir / passer un argument au gestionnaire de signaux

Puis-je fournir/transmettre des arguments au gestionnaire de signaux?

/* Signal handling */
struct sigaction act;
act.sa_handler = signal_handler;
/* some more settings */

Maintenant, le gestionnaire ressemble à ceci:

void signal_handler(int signo) {
    /* some code */
}

Si je veux faire quelque chose de spécial, c'est-à-dire supprimer des fichiers temporaires, puis-je fournir ces fichiers comme argument à ce gestionnaire?

Edit 0: Merci pour les réponses. Nous évitons/déconseillons généralement l'utilisation de variables globales. Et dans ce cas, si vous avez un énorme programme, les choses peuvent mal tourner à différents endroits et vous devrez peut-être faire beaucoup de nettoyage. Pourquoi l'API a-t-elle été conçue de cette façon?

54
hari

Vous ne pouvez pas transmettre vos propres données au gestionnaire de signaux en tant que paramètres. Au lieu de cela, vous devrez stocker vos paramètres dans des variables globales. (Et soyez vraiment, vraiment prudent si vous avez besoin de changer ces données après avoir installé le gestionnaire de signal).

Réponse pour modifier 0: Raisons historiques. Les signaux sont une conception très ancienne et vraiment de bas niveau. Fondamentalement, vous venez de donner au noyau une seule adresse pour un code machine et lui demandez d'aller à cette adresse spécifique si tel ou tel se produit. Nous sommes de retour dans la mentalité de "l'assembleur portable", où les noyaux fournissent un service de base sans fioritures, et quel que soit le processus utilisateur auquel on peut raisonnablement s'attendre, il doit le faire lui-même.

De plus, les arguments habituels contre les variables globales ne s'appliquent pas vraiment ici. Le gestionnaire de signaux lui-même est un paramètre global, il n'y a donc pas de possibilité pertinente d'avoir plusieurs ensembles différents de paramètres spécifiés par l'utilisateur pour lui. (Eh bien, en fait, ce n'est pas entièrement global, mais uniquement thread-global. Mais l'API de threading inclura un mécanisme de stockage thread-local, ce qui est exactement ce dont vous avez besoin dans ce cas).

52
Henning Makholm

Un enregistrement de gestionnaire de signal est déjà un état global équivalent aux variables globales. Il n'est donc pas plus grave d'utiliser des variables globales pour lui passer des arguments. Cependant, c'est une énorme erreur (presque certainement comportement indéfini sauf si vous êtes un expert!) De toute façon, faire quoi que ce soit à partir d'un gestionnaire de signaux. Si vous bloquez simplement les signaux et les interrogez à partir de votre boucle de programme principale, vous pouvez éviter tous ces problèmes.

16
R..

C'est une très vieille question mais je pense que je peux vous montrer un bon truc qui aurait répondu à votre problème. Pas besoin d'utiliser sigqueue ou autre.

Je n'aime pas non plus l'utilisation de variables globales, j'ai donc dû trouver un moyen intelligent, dans mon cas, d'envoyer un ptr vide (que vous pourrez ensuite transtyper en fonction de vos besoins).

En fait, vous pouvez le faire:

signal(SIGWHATEVER, (void (*)(int))sighandler); // Yes it works ! Even with -Wall -Wextra -Werror using gcc

Ensuite, votre sighandler ressemblerait à ceci:

int sighandler(const int signal, void *ptr) // Actually void can be replaced with anything you want , MAGIC !

Vous pourriez vous demander: comment obtenir le * ptr alors?

Voici comment: à l'initialisation

signal(SIGWHATEVER, (void (*)(int))sighandler)
sighandler(FAKE_SIGNAL, your_ptr);

Dans votre fonction sighandler:

int sighandler(const int signal, void *ptr)
{
  static my_struct saved = NULL;

  if (saved == NULL)
     saved = ptr;
  if (signal == SIGNALWHATEVER)
     // DO YOUR STUFF OR FREE YOUR PTR
   return (0);
}
5
rak007

Absolument. Vous pouvez passer des entiers et des pointeurs aux gestionnaires de signaux en utilisant sigqueue () au lieu du kill () habituel.

http://man7.org/linux/man-pages/man2/sigqueue.2.html

4
David Yeager

Stockez les noms des fichiers dans une variable globale, puis accédez-y à partir du gestionnaire. Le rappel du gestionnaire de signaux ne recevra qu'un seul argument: l'ID du signal réel qui a causé le problème (par exemple SIGINT, SIGTSTP)

Edit 0: "Il doit y avoir une raison solide pour ne pas autoriser les arguments au gestionnaire." <- Il y a un vecteur d'interruption (fondamentalement, un ensemble d'adresses de saut vers des routines pour chaque signal possible). Étant donné la façon dont l'interruption est déclenchée, en fonction du vecteur d'interruption, une fonction particulière est appelée. Malheureusement, il n'est pas clair où la mémoire associée à la variable sera appelée, et en fonction de l'interruption, la mémoire peut être corrompue. Il existe un moyen de le contourner, mais vous ne pouvez pas tirer parti de l'instruction d'assemblage int 0x80 existante (que certains systèmes utilisent toujours)

1
Foo Bah

Vous pouvez utiliser un gestionnaire de signal qui est une méthode d'une classe. Ensuite, ce gestionnaire peut accéder aux données des membres de cette classe. Je ne suis pas tout à fait sûr de ce que Python fait sous les couvertures ici autour de l'appel C signal (), mais ce doit être une nouvelle portée des données?

J'ai été étonné que cela fonctionne, mais cela fonctionne. Exécutez-le, puis supprimez le processus à partir d'un autre terminal.

import os, signal, time

class someclass:
    def __init__(self):
        self.myvalue = "something initialized not globally defined"
        signal.signal(signal.SIGTERM, self.myHandler)
    def myHandler(self, s, f):
        # WTF u can do this?
        print "HEY I CAUGHT IT, AND CHECK THIS OUT", self.myvalue


print "Making an object"
a = someclass()

while 1:
    print "sleeping.  Kill me now."
    time.sleep(60)
0
drizzo4shizzo