Puis-je exécuter free () ou close () dans un gestionnaire de signaux? [dupliquer]
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+C
interruption pour exécuter la signal_handler
fonction une fois au lieu de tuer le processus tout de suite. J'ai lu quelque part que certaines fonctions comme pourrait être free
ne sont pas asynchrones et ne s'exécuteraient PAS dans le, signal_handler
mais je n'en suis pas sûr.
Puis - je exécuter des fonctions comme free
, close
, exit
ou même pthread_join
dans un gestionnaire de signal?
Réponses
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.
close
est répertorié et doit être sûr. free
n'est pas. Pour des raisons pour lesquelles vous devriez regarder son code source (il contient des verrous). exit
n'est pas sûr car il peut appeler des gestionnaires de nettoyage arbitraires. Vous avez _exit
qui sort brusquement sans le nettoyage.
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.
La réponse courte est non:
7.1.4 Utilisation des fonctions de bibliothèqueÉbauche en ligne C 2011
...
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
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.
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) .