points d'annulation dans les gestionnaires de signal?
-
26-09-2019 - |
Question
Qu'est-ce qui se passe si un programme appelle une fonction qui est un point d'annulation d'un gestionnaire de signal? Il y a un certain nombre de fonctions qui spécifie que les deux POSIX points de signal-async de sécurité et d'annulation. Si un gestionnaire de signal appelle une telle fonction et l'annulation est sollicité, le résultat est tout à fait semblable à ce qui se passerait si le fil avait permis l'annulation asynchrone - bien pire, parce que tous les gestionnaires de nettoyage d'annulation, qui ne sont probablement pas async-SIGNAL sûr, serait appelé à partir d'un contexte signal gestionnaire.
Qu'est-ce que fait préciser Posix dans ce cas, et qu'est-ce que les mises en œuvre font réellement? Je ne peux trouver aucune langue POSIX qui interdit les points d'annulation dans les gestionnaires de signaux d'agir conformément à, ni une telle protection dans la glibc / nptl la source.
La solution
Je ne suis pas au courant que ose même Posix mentionner ce sujet, mais je ne l'ai pas fait une recherche exhaustive.
Quelques brèves expériences avec un système de gcc / nptl révèle que, comme je doutais et je pense que vous aussi, il n'y a pas une telle protection dans NPTL -. Les gestionnaires d'annulation ne s'appellent en effet, à partir du contexte du gestionnaire de signaux
Le programme ci-dessous (excuses pour le hackiness etc) affiche la sortie suivante:
Signal handler called
Sent cancellation
Cleanup called
In sighandler
... ce qui indique que:
- le gestionnaire de signaux été appelé
- l'autre thread alors appelé
pthread_cancel()
- le gestionnaire d'annulation puis s'appelé, sans le gestionnaire de signal terminé
Voici le programme:
#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
pthread_t mainthread;
int in_sighandler = 0;
void
cleanup (void *arg)
{
write(1, "Cleanup called\n", strlen("Cleanup called\n"));
if (in_sighandler) {
write(1, "In sighandler\n", strlen("In sighandler\n"));
} else {
write(1, "Not in sighandler\n", strlen("In sighandler\n"));
}
}
void
sighandler (int sig, siginfo_t *siginfo, void *arg)
{
in_sighandler = 1;
write(1,"Signal handler called\n", strlen("Signal handler called\n")); // write() is a CP
usleep(3000000); // usleep() is a CP; not strictly async-signal-safe but happens to be so in Linux
write(1, "Signal handler exit\n", strlen("Signal handler exit\n"));
in_sighandler = 0;
}
void *
thread (void *arg)
{
sleep(1);
pthread_kill(mainthread, SIGUSR1);
usleep(500000);
pthread_cancel(mainthread);
printf("Sent cancellation\n");
return (NULL);
}
int
main (int argc, char **argv)
{
int rc;
struct sigaction sa;
pthread_t threadid;
mainthread = pthread_self();
// Set up a signal handler to test its cancellation properties
sa.sa_sigaction = &sighandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
rc = sigaction(SIGUSR1, &sa, NULL);
assert(rc == 0);
// Set up a thread to send us signals and cancel us
rc = pthread_create(&threadid, NULL, &thread, NULL);
assert(rc == 0);
// Set up cleanup handlers and loop forever
pthread_cleanup_push(&cleanup, NULL);
while (1) {
sleep(60);
}
pthread_cleanup_pop(0);
return (0);
}