Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Traitement des signaux asynchrones

47 réponses
Avatar
JKB
Bonjour à tous,

J'ai un petit doute sur la gestion des signaux POSIX installés grâce
à sigaction(). J'ai accès à un certain nombre d'Unix dont Linux,
Tru64, Solaris, NetBSD, OpenBSD et FreeBSD. Et NetBSD me pose un
petit problème. Je n'arrive pas à savoir s'il s'agit d'un problème
dans mon code ou d'u truc qui m'aurait échappé.

Considérons le code suivant :

do
{
code_retour = nanosleep(&temporisation, &temporisation);
erreur = errno;

...
...
} while((code_retour == -1) && (erreur == EINTR));

Je simplifie. J'interromps nanosleep() grâce à un SIGINT (ctrl+C) et
je vois le gestionnaire se lancer (j'ai collé un printf() dans le
gestionnaire en question). Sous NetBSD, le gestionnaire de signal
est lancé dans un thread spécifique (et non le thread de
l'appelant). Ça ne semble pas être en contradiction avec les specs
POSIX. Mais nanosleep() sort avec un code d'erreur EINTR _avant_ que
le signal soit effectivement traité. Or dans la suite des
instructions de la boucle se trouve un test sur une variable volatile
et atomique positionnée par ce gestionnaire de signal. La plupart du
temps, ce test échoue car le gestionnaire de signal s'exécutant dans
un autre thread n'a pas encore positionné la variable à la bonne
valeur.

D'où une série de questions ;-)
1/ Est-ce un comportement attendu ou un bug de la gestion des
signaux de NetBSD ? Je n'observe ce comportement que sous ce
système.
2/ Comment s'assurer de façon propre que le gestionnaire de signal
a terminé ses traitement sans y coller un sem_post() (j'aimerais
éviter) ?

Merci de votre attention,

JKB

PS: Nicolas George, pas la peine de répondre, je t'ai plonké depuis
longtemps.

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr

10 réponses

1 2 3 4 5
Avatar
talon
JKB wrote:

C'est très gentil à toi, mais ça ne répond pas à la question posée.
Je sais parfaitement utiliser les pthreads (y compris les signaux,
les masques de signaux et autres joyeusetés). J'ai juste un
problème, SIGINT arrête un peu trop brutalement un programme. Tu me
réponds qu'il faut utiliser des sémaphores, ce qui n'est pas
possible dans mon cas (je ne sais pas quand l'utilisateur aura
l'idée d'envoyer un CTRL+C, donc je ne peux pas protéger cet appel
asynchrone par un sémaphore).



Une recherche Google sur pthreads + SIGINT te donne une floppée de
résultats montrant que le problème est complexe. Par exemple:
http://stackoverflow.com/questions/6621785/posix-pthread-programming


First of all, signal handlers are a pain. Unless you're very careful,
you have to assume most library functions are not legal to call from a
signal handler. Fortunately, sem_post is specified to be
async-signal-safe, and can meet your requirements perfectly:

At the beginning of your program, initialize a semaphore with
sem_init(&exit_sem, 0, 0);
Install a signal handler for SIGINT (and any other termination signals
you want to handle, like SIGTERM) that performs sem_post(&exit_sem); and
returns.
Replace the for(;;); in the main thread with while
(sem_wait(&exit_sem)!=0).
After sem_wait succeeds, the main thread should inform all other threads
that they should exit, then wait for them all to exit.


--

Michel TALON
Avatar
JKB
Le Tue, 9 Oct 2012 14:36:49 +0000 (UTC),
Michel Talon écrivait :
JKB wrote:

C'est très gentil à toi, mais ça ne répond pas à la question posée.
Je sais parfaitement utiliser les pthreads (y compris les signaux,
les masques de signaux et autres joyeusetés). J'ai juste un
problème, SIGINT arrête un peu trop brutalement un programme. Tu me
réponds qu'il faut utiliser des sémaphores, ce qui n'est pas
possible dans mon cas (je ne sais pas quand l'utilisateur aura
l'idée d'envoyer un CTRL+C, donc je ne peux pas protéger cet appel
asynchrone par un sémaphore).



Une recherche Google sur pthreads + SIGINT te donne une floppée de
résultats montrant que le problème est complexe. Par exemple:
http://stackoverflow.com/questions/6621785/posix-pthread-programming


First of all, signal handlers are a pain. Unless you're very careful,
you have to assume most library functions are not legal to call from a
signal handler. Fortunately, sem_post is specified to be
async-signal-safe, and can meet your requirements perfectly:

At the beginning of your program, initialize a semaphore with
sem_init(&exit_sem, 0, 0);
Install a signal handler for SIGINT (and any other termination signals
you want to handle, like SIGTERM) that performs sem_post(&exit_sem); and
returns.
Replace the for(;;); in the main thread with while
(sem_wait(&exit_sem)!=0).
After sem_wait succeeds, the main thread should inform all other threads
that they should exit, then wait for them all to exit.



Est-ce que tu peux lire une bonne fois pour toute les données du
problème avant de répondre n'importe quoi à côté de la plaque ?
1/ mon programme de test est monothreadé (parce qu'on verra le côté
multithreadé dans un second temps) ;
2/ je sais parfaitement utiliser un sémaphore (et des programmes
comme ton exemple, c'est l'enfance de l'art. Merci de me prendre
pour un débile profond);
3/ ça ne résiste pas au singe qui appuie plusieurs fois sur CTRL+C
parce qu'il faudrait voir à ne pas oublier de remettre le sémaphore
à un état nul et c'est là qu'il y a un problème au passage
monothread vers multithread ;
4/ je ne sais pas si tu as bien remarqué l'exemple fourni au début
de ce fil, il y avait des points de suspensions, parce que le
programme n'a pas forcément le droit de s'interrompre n'importe
quand (il y a des ressources à libérer dans un certain ordre) et que le
nanosleep() qui pose problème est justement dans une telle boucle.

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
talon
JKB wrote:
3/ ça ne résiste pas au singe qui appuie plusieurs fois sur CTRL+C





Si tu cherches pthread + SIGINT dans Google tu peux voir que le premier
message qui arrive porte justement sur ce problème, un seul Ctrl-C
marche, mais une ribambelle en série plante (sous Linux).
Il suit une tonne de commentaires sur la manière de résoudre ce
problème, dont semble t'il la solution qui se dégage est de commencer
dans le programme principal par
- lancer un thread (qui aura pour seule fonction de traîter les signaux)
- puis bloquer tous les signaux intéressants
- puis lancer les threads qui font le boulot. Ils vont hériter à leur
naissance du bloquage des signaux. Bloquer les signaux dans le thread
serait une erreur car soumis à une possibilité de race.

Dans cette situation tu peux être sûr que seul le premier thread va
recevoir les signaux, et tu les traîtes comme tu veux. Si ça doit
terminer l'ensemble, ce thread doit signaler à tous les autres de faire
le nettoyage et mourir, par exemple en modifiant une variable partagée.

parce qu'il faudrait voir à ne pas oublier de remettre le sémaphore
à un état nul et c'est là qu'il y a un problème au passage
monothread vers multithread ;
4/ je ne sais pas si tu as bien remarqué l'exemple fourni au début
de ce fil, il y avait des points de suspensions, parce que le
programme n'a pas forcément le droit de s'interrompre n'importe
quand (il y a des ressources à libérer dans un certain ordre) et que le
nanosleep() qui pose problème est justement dans une telle boucle.



Et justement le message que je t'ai indiqué explique comment terminer
proprement l'ensemble des threads. Si tu peux éviter un sémaphore tant
mieux, je ne vois que des emmerdes avec ces trucs là.





--

Michel TALON
Avatar
JKB
Le Tue, 9 Oct 2012 15:27:26 +0000 (UTC),
Michel Talon écrivait :
JKB wrote:
3/ ça ne résiste pas au singe qui appuie plusieurs fois sur CTRL+C





Si tu cherches pthread + SIGINT dans Google tu peux voir que le premier
message qui arrive porte justement sur ce problème, un seul Ctrl-C
marche, mais une ribambelle en série plante (sous Linux).
Il suit une tonne de commentaires sur la manière de résoudre ce
problème, dont semble t'il la solution qui se dégage est de commencer
dans le programme principal par
- lancer un thread (qui aura pour seule fonction de traîter les signaux)
- puis bloquer tous les signaux intéressants
- puis lancer les threads qui font le boulot. Ils vont hériter à leur
naissance du bloquage des signaux. Bloquer les signaux dans le thread
serait une erreur car soumis à une possibilité de race.

Dans cette situation tu peux être sûr que seul le premier thread va
recevoir les signaux, et tu les traîtes comme tu veux. Si ça doit
terminer l'ensemble, ce thread doit signaler à tous les autres de faire
le nettoyage et mourir, par exemple en modifiant une variable partagée.

parce qu'il faudrait voir à ne pas oublier de remettre le sémaphore
à un état nul et c'est là qu'il y a un problème au passage
monothread vers multithread ;
4/ je ne sais pas si tu as bien remarqué l'exemple fourni au début
de ce fil, il y avait des points de suspensions, parce que le
programme n'a pas forcément le droit de s'interrompre n'importe
quand (il y a des ressources à libérer dans un certain ordre) et que le
nanosleep() qui pose problème est justement dans une telle boucle.



Et justement le message que je t'ai indiqué explique comment terminer
proprement l'ensemble des threads. Si tu peux éviter un sémaphore tant
mieux, je ne vois que des emmerdes avec ces trucs là.



Je vais m'arrêter avec toi. Apprends à lire avant de répondre.

Fin de la discussion.

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
Paul Gaborit
À (at) Tue, 9 Oct 2012 14:36:49 +0000 (UTC),
(Michel Talon) écrivait (wrote):

Une recherche Google sur pthreads + SIGINT te donne une floppée de
résultats montrant que le problème est complexe. Par exemple:
http://stackoverflow.com/questions/6621785/posix-pthread-programming


First of all, signal handlers are a pain. Unless you're very careful,
you have to assume most library functions are not legal to call from a
signal handler. Fortunately, sem_post is specified to be
async-signal-safe, and can meet your requirements perfectly:

At the beginning of your program, initialize a semaphore with
sem_init(&exit_sem, 0, 0);
Install a signal handler for SIGINT (and any other termination signals
you want to handle, like SIGTERM) that performs sem_post(&exit_sem); and
returns.
Replace the for(;;); in the main thread with while
(sem_wait(&exit_sem)!=0).
After sem_wait succeeds, the main thread should inform all other threads
that they should exit, then wait for them all to exit.



Juste pour comprendre: le comportement décrit ici est celui de n'importe
quel processus ou ce comportement ne concerne que les processus
utilisant la bibliothèque pthreads ?

--
Paul Gaborit - <http://perso.mines-albi.fr/~gaborit/>
Avatar
JKB
Le Tue, 09 Oct 2012 20:06:05 +0200,
Paul Gaborit écrivait :

À (at) Tue, 9 Oct 2012 14:36:49 +0000 (UTC),
(Michel Talon) écrivait (wrote):

Une recherche Google sur pthreads + SIGINT te donne une floppée de
résultats montrant que le problème est complexe. Par exemple:
http://stackoverflow.com/questions/6621785/posix-pthread-programming


First of all, signal handlers are a pain. Unless you're very careful,
you have to assume most library functions are not legal to call from a
signal handler. Fortunately, sem_post is specified to be
async-signal-safe, and can meet your requirements perfectly:

At the beginning of your program, initialize a semaphore with
sem_init(&exit_sem, 0, 0);
Install a signal handler for SIGINT (and any other termination signals
you want to handle, like SIGTERM) that performs sem_post(&exit_sem); and
returns.
Replace the for(;;); in the main thread with while
(sem_wait(&exit_sem)!=0).
After sem_wait succeeds, the main thread should inform all other threads
that they should exit, then wait for them all to exit.



Juste pour comprendre: le comportement décrit ici est celui de n'importe
quel processus ou ce comportement ne concerne que les processus
utilisant la bibliothèque pthreads ?



C'est la façon canonique de faire lorsqu'on n'est pas contraint à
récupérer des signaux dans les threads. Ça simplifie la gestion,
mais ça n'est pas toujours faisable.

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
Alain Ketterlin
Paul Gaborit writes:

[...]
First of all, signal handlers are a pain. Unless you're very careful,
you have to assume most library functions are not legal to call from a
signal handler. Fortunately, sem_post is specified to be
async-signal-safe, and can meet your requirements perfectly:

At the beginning of your program, initialize a semaphore with
sem_init(&exit_sem, 0, 0);
Install a signal handler for SIGINT (and any other termination signals
you want to handle, like SIGTERM) that performs sem_post(&exit_sem); and
returns.
Replace the for(;;); in the main thread with while
(sem_wait(&exit_sem)!=0).
After sem_wait succeeds, the main thread should inform all other threads
that they should exit, then wait for them all to exit.



Juste pour comprendre: le comportement décrit ici est celui de n'imp orte
quel processus ou ce comportement ne concerne que les processus
utilisant la bibliothèque pthreads ?



Ce n'est normalement nécessaire que dans les processus qui utilisent d es
threads. Dans le cas contraire, il n'y a pas de concurrence, le
gestionnaire fait sa cuisine, et quand le programme reprend son cours il
voit l'effet du gestionnaire (typiquement, positionner un flag).

Le problème, c'est que JKB est tombé sur un cas où il n'a pa s demandé de
thread mais en a quand même. Du coup il a tous les inconvénients, sans
les avantages : la séquence est brisée, son programme reprend son cours
avant que le gestionnaire ait fini son travail.

-- Alain.
Avatar
Paul Gaborit
À (at) Tue, 09 Oct 2012 21:51:15 +0200,
Alain Ketterlin écrivait (wrote):

Paul Gaborit writes:

[...]
First of all, signal handlers are a pain. Unless you're very careful,
you have to assume most library functions are not legal to call from a
signal handler. Fortunately, sem_post is specified to be
async-signal-safe, and can meet your requirements perfectly:

At the beginning of your program, initialize a semaphore with
sem_init(&exit_sem, 0, 0);
Install a signal handler for SIGINT (and any other termination signals
you want to handle, like SIGTERM) that performs sem_post(&exit_sem); and
returns.
Replace the for(;;); in the main thread with while
(sem_wait(&exit_sem)!=0).
After sem_wait succeeds, the main thread should inform all other threads
that they should exit, then wait for them all to exit.



Juste pour comprendre: le comportement décrit ici est celui de n'impor te
quel processus ou ce comportement ne concerne que les processus
utilisant la bibliothèque pthreads ?



Ce n'est normalement nécessaire que dans les processus qui utilisent des
threads. Dans le cas contraire, il n'y a pas de concurrence, le
gestionnaire fait sa cuisine, et quand le programme reprend son cours il
voit l'effet du gestionnaire (typiquement, positionner un flag).

Le problème, c'est que JKB est tombé sur un cas où il n'a pas deman dé de
thread mais en a quand même. Du coup il a tous les inconvénients, sans
les avantages : la séquence est brisée, son programme reprend son cou rs
avant que le gestionnaire ait fini son travail.



Je pense bien avoir compris la situation.

Ce que j'aimerai savoir c'est, lorsqu'on écrit un programme, quel est le
critère pour déterminer s'il va tomber dans l'un ou l'autre des deux cas
(le cas "normal" ou il n'y a pas concurrence et celui où le signal est
traité par un thread).

--
Paul Gaborit - <http://perso.mines-albi.fr/~gaborit/>
Avatar
JKB
Le Wed, 10 Oct 2012 02:02:53 +0200,
Paul Gaborit écrivait :

À (at) Tue, 09 Oct 2012 21:51:15 +0200,
Alain Ketterlin écrivait (wrote):

Paul Gaborit writes:

[...]
First of all, signal handlers are a pain. Unless you're very careful,
you have to assume most library functions are not legal to call from a
signal handler. Fortunately, sem_post is specified to be
async-signal-safe, and can meet your requirements perfectly:

At the beginning of your program, initialize a semaphore with
sem_init(&exit_sem, 0, 0);
Install a signal handler for SIGINT (and any other termination signals
you want to handle, like SIGTERM) that performs sem_post(&exit_sem); and
returns.
Replace the for(;;); in the main thread with while
(sem_wait(&exit_sem)!=0).
After sem_wait succeeds, the main thread should inform all other threads
that they should exit, then wait for them all to exit.



Juste pour comprendre: le comportement décrit ici est celui de n'importe
quel processus ou ce comportement ne concerne que les processus
utilisant la bibliothèque pthreads ?



Ce n'est normalement nécessaire que dans les processus qui utilisent des
threads. Dans le cas contraire, il n'y a pas de concurrence, le
gestionnaire fait sa cuisine, et quand le programme reprend son cours il
voit l'effet du gestionnaire (typiquement, positionner un flag).

Le problème, c'est que JKB est tombé sur un cas où il n'a pas demandé de
thread mais en a quand même. Du coup il a tous les inconvénients, sans
les avantages : la séquence est brisée, son programme reprend son cours
avant que le gestionnaire ait fini son travail.



Je pense bien avoir compris la situation.

Ce que j'aimerai savoir c'est, lorsqu'on écrit un programme, quel est le
critère pour déterminer s'il va tomber dans l'un ou l'autre des deux cas
(le cas "normal" ou il n'y a pas concurrence et celui où le signal est
traité par un thread).



Si je le savais, ça résoudrait la chose ;-)

Avant de poster ici, j'ai passé en revue tous les paramètres de
sigaction() pour voir si par hasard je n'avais pas oublié quelque
chose, mais aucun paramètre ne permet de choisir le comportement.
J'ai l'impression que ça dépend de la façon de voir des développeurs
de la libc.

Cordialement,

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
Damien Wyart
* JKB in fr.comp.os.unix:
[...] Je suis en train de creuser le truc avec les gens de NetBSD,
mais pour l'instant, rien ne se dessine.



En privé ? J'ai rien vu sur les listes de diffusion...

--
DW
1 2 3 4 5