Puis-je exécuter free () ou close () dans un gestionnaire de signaux? [dupliquer]

Dec 10 2020

J'ai un code qui ressemble à ceci:

//global variables

void signal_handler() {
    //deallocation of global variables
    free(foo);
    close(foo_2);
    exit(0);
}

int main () {
    signal(SIGINT, signal_handler);

    //irrelevant code
}

Comme vous pouvez le voir, j'ai changé l' CTRL+Cinterruption pour exécuter la signal_handlerfonction une fois au lieu de tuer le processus tout de suite. J'ai lu quelque part que certaines fonctions comme pourrait être freene sont pas asynchrones et ne s'exécuteraient PAS dans le, signal_handlermais je n'en suis pas sûr.

Puis - je exécuter des fonctions comme free, close, exitou même pthread_joindans un gestionnaire de signal?

Réponses

1 dratenik Dec 10 2020 at 15:46

Non. Seules les fonctions répertoriées dans man 7 signal-safety peuvent être appelées en toute sécurité dans un gestionnaire de signaux.

closeest répertorié et doit être sûr. freen'est pas. Pour des raisons pour lesquelles vous devriez regarder son code source (il contient des verrous). exitn'est pas sûr car il peut appeler des gestionnaires de nettoyage arbitraires. Vous avez _exitqui sort brusquement sans le nettoyage.

1 MarcoBonelli Dec 10 2020 at 15:46

Vous pouvez techniquement compiler un programme qui appelle de telles fonctions dans un gestionnaire de signaux, rien ne vous empêche de le faire. Cependant, cela entraînera un comportement indéfini si la fonction que vous essayez d'exécuter n'est pas sûre pour les signaux asynchrones. Ce n'est pas comme si une fonction unsafe "ne s'exécutait pas" comme vous le dites, elle le pourrait très bien, mais ce serait toujours un comportement indéfini.

Une liste des fonctions de sécurité des signaux asynchrones est documentée dans man 7 signal-safety. La close()fonction est sûre, alors que free()et phtread_join()ne sont pas. L' appel de la fonction à partir d'un gestionnaire de signaux exit()n'est pas non plus sûr, si vous souhaitez quitter ce contexte, vous devrez le faire à la _exit()place.

La seule façon d'appeler en toute sécurité une fonction qui n'est pas sécurisée pour le signal asynchrone lors de la réception d'un signal est de "se souvenir" que vous devez l'appeler (par exemple en définissant une variable globale) et de le faire après le retour du gestionnaire de signal.

1 JohnBode Dec 10 2020 at 16:30

La réponse courte est non:

7.1.4 Utilisation des fonctions de bibliothèque
...
4 Les fonctions de la bibliothèque standard ne sont pas garanties d'être réentrantes et peuvent modifier des objets avec une durée de stockage statique ou de thread. 188)
188) Ainsi, un gestionnaire de signaux ne peut pas, en général, appeler des fonctions de bibliothèque standard
Ébauche en ligne C 2011

Exemple concret des conséquences - j'ai travaillé sur un système qui communiquait avec une base de données Access. Il y avait un gestionnaire de signal qui a essayé d'écrire un message d'erreur sur la console avec fprintf, mais d'une manière ou d'une autre, pendant le processus de gestion du signal, il a stderrété mappé vers le fichier .mdb qui stockait la base de données, écrasant l'en-tête et ruinant la base de données de manière irréparable.

Honnêtement, il n'y a pas grand-chose que vous puissiez faire dans un gestionnaire de signaux à part définir un indicateur à vérifier ailleurs.

BasileStarynkevitch Dec 10 2020 at 16:24

Puis-je exécuter free () ou close () dans un gestionnaire de signaux?

Vous ne devriez certainement pas. Voir signal (7) et sécurité du signal (7)

En pratique, cela pourrait fonctionner comme vous le souhaitez peut-être plus de la moitié du temps. IIRC, le compilateur GCC fait ce que vous voulez, et cela fonctionne généralement.

Une meilleure approche est d'utiliser du write (2) sur un tube (7) (depuis l'intérieur de votre gestionnaire de signaux) et de vérifier de temps en temps ce tube (dans votre programme principal) avec poll (2) ou des choses connexes.

Ou vous pouvez en définir volatile sigatomic_t flag;(peut-être qu'il devrait l'être également _Atomic) dans votre gestionnaire de signaux, et vérifier cet indicateur ailleurs (dans le programme principal, en dehors des gestionnaires de signaux).

Qt explique cela mieux que je ne pourrais le faire en quelques minutes.

Sous Linux, voir aussi signalfd (2) et eventfd (2) .