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
JKB
Le Thu, 11 Oct 2012 14:12:55 +0200,
Lucas Levrel écrivait :
Le 11 octobre 2012, JKB a écrit :

Utiliser une variable ne serait possible qu'en utilisant les
variables globales par thread, mais ces fonctions ne peuvent pas
être appelées depuis un gestionnaire de signal...



Et un « bête » tableau global (à chaque thread sa case) ? Je suppose que
le nombre de threads est variable... Une liste chaînée ?



pthread_self() n'est pas async signal safe. J'y avais déjà pensé...
Par ailleurs, si sous Solaris, les TID sont des entiers consécutifs
à la manière des PID, sous Linux, il s'agit de pointeurs (sans
vérification, ce qui fait qu'un pthread_kill() sur un thread
inexistant ne retourne pas une erreur mais un segfault :-( ).
Difficile d'en faire un tableau. Tout au plus une liste chaînée.

Mais merci pour la suggestion.

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
Bruno Ducrot
On 08-10-2012, JKB wrote:
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) ?



Un truc bien crade, genre setcontext((ucontext_t*) context) à la fin
du handler, ca ne pourrait pas le faire ?

--
Bruno Ducrot

A quoi ca sert que Ducrot hisse des carcasses ?
Avatar
JKB
Le 11 Oct 2012 14:01:14 GMT,
Bruno Ducrot écrivait :
On 08-10-2012, JKB wrote:
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) ?



Un truc bien crade, genre setcontext((ucontext_t*) context) à la fin
du handler, ca ne pourrait pas le faire ?



Dans un gestionnaire de signal ? Je ne m'y risquerais pas ;-)

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
Lucas Levrel
Le 11 octobre 2012, JKB a écrit :

Le Thu, 11 Oct 2012 14:12:55 +0200,
Lucas Levrel écrivait :
Et un « bête » tableau global (à chaque thread sa case) ? Je suppose que
le nombre de threads est variable... Une liste chaînée ?



pthread_self() n'est pas async signal safe. J'y avais déjà pensé...



Mais quand tu crées un thread, tu peux lui passer un identifiant qui sera
son index dans le tableau, non ?

Par ailleurs, si sous Solaris, les TID sont des entiers consécutifs
à la manière des PID, sous Linux, il s'agit de pointeurs (sans
vérification, ce qui fait qu'un pthread_kill() sur un thread
inexistant ne retourne pas une erreur mais un segfault :-( ).
Difficile d'en faire un tableau. Tout au plus une liste chaînée.

Mais merci pour la suggestion.



Merci de me supporter :-)

--
LL
Avatar
JKB
Le Thu, 11 Oct 2012 17:40:14 +0200,
Lucas Levrel écrivait :
Le 11 octobre 2012, JKB a écrit :

Le Thu, 11 Oct 2012 14:12:55 +0200,
Lucas Levrel écrivait :
Et un « bête » tableau global (à chaque thread sa case) ? Je suppose que
le nombre de threads est variable... Une liste chaînée ?



pthread_self() n'est pas async signal safe. J'y avais déjà pensé...



Mais quand tu crées un thread, tu peux lui passer un identifiant qui sera
son index dans le tableau, non ?



Oui, mais après ?

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
Bruno Ducrot
On 11-10-2012, JKB wrote:
Le 11 Oct 2012 14:01:14 GMT,
Bruno Ducrot écrivait :
On 08-10-2012, JKB wrote:
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) ?



Un truc bien crade, genre setcontext((ucontext_t*) context) à la fin
du handler, ca ne pourrait pas le faire ?



Dans un gestionnaire de signal ? Je ne m'y risquerais pas ;-)



http://pubs.opengroup.org/onlinepubs/7908799/xsh/getcontext.html

<cite>
APPLICATION USAGE

When a signal handler is executed, the current user context is
saved and a new context is created. If the thread leaves the signal
handler via longjmp(), then it is unspecified whether the context
at the time of the corresponding setjmp() call is restored and thus
whether future calls to getcontext() will provide an accurate representation
of the current context, since the context restored by longjmp() does
not necessarily contain all the information that setcontext() requires.
Signal handlers should use siglongjmp() or setcontext() instead.
</cite>

Je viens de tester :

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <ucontext.h>

static volatile sig_atomic_t t = 0;

static void handler(int signum, siginfo_t *info, void *uct)
{
printf("handlern");
t = 1;
setcontext(uct);
printf("setcontext failedn");
}

int
main(void)
{
int rv, err;
struct timespec tp;
struct sigaction sa;


sa.sa_sigaction = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO | SA_RESTART;

if (sigaction(SIGINT, &sa, NULL) == -1)
return 1;

tp.tv_sec = 1;
tp.tv_nsec = 0;

do {
rv = nanosleep(&tp, &tp);
err = errno;


} while(rv == -1 && errno == EINTR);

printf("%dn", (int) t);

return 0;
}


et ça a l'air de marcher :

~> gcc test_signal.c -Wall -Wextra
test_signal.c: In function ‘handler’:
test_signal.c:10: warning: unused parameter ‘signum’
test_signal.c:10: warning: unused parameter ‘info’
~> ./a.out
0
~> ./a.out
^Chandler
1
~>


A plus,

--
Bruno Ducrot

A quoi ca sert que Ducrot hisse des carcasses ?
Avatar
JKB
Le 12 Oct 2012 09:35:22 GMT,
Bruno Ducrot écrivait :
On 11-10-2012, JKB wrote:
Le 11 Oct 2012 14:01:14 GMT,
Bruno Ducrot écrivait :
On 08-10-2012, JKB wrote:
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) ?



Un truc bien crade, genre setcontext((ucontext_t*) context) à la fin
du handler, ca ne pourrait pas le faire ?



Dans un gestionnaire de signal ? Je ne m'y risquerais pas ;-)



http://pubs.opengroup.org/onlinepubs/7908799/xsh/getcontext.html

<cite>
APPLICATION USAGE

When a signal handler is executed, the current user context is
saved and a new context is created. If the thread leaves the signal
handler via longjmp(), then it is unspecified whether the context
at the time of the corresponding setjmp() call is restored and thus
whether future calls to getcontext() will provide an accurate representation
of the current context, since the context restored by longjmp() does
not necessarily contain all the information that setcontext() requires.
Signal handlers should use siglongjmp() or setcontext() instead.
</cite>



Mouais, je me méfie. Lorsqu'une fonction n'est pas async signal
safe, je ne sais pas pourquoi, mais je n'aime pas trop la coller
dans un gestionnaire de signal...

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
Lucas Levrel
Le 11 octobre 2012, JKB a écrit :

Le Thu, 11 Oct 2012 17:40:14 +0200,
Lucas Levrel écrivait :
Le 11 octobre 2012, JKB a écrit :

Le Thu, 11 Oct 2012 14:12:55 +0200,
Lucas Levrel écrivait :
Et un « bête » tableau global (à chaque thread sa case) ? Je suppose que
le nombre de threads est variable... Une liste chaînée ?



pthread_self() n'est pas async signal safe. J'y avais déjà pensé...



Mais quand tu crées un thread, tu peux lui passer un identifiant qui sera
son index dans le tableau, non ?



Oui, mais après ?



Eh bien je ne vois pas pourquoi tu as besoin de pthread_self. Est-ce que
l'identifiant « maison » ne suffit pas ?

Sinon, quand un thread est créé, le créateur lui passe un identifiant
« maison » et récupère son pointeur (1er argument de pthread_create) ; il
met alors la paire identifiant-pointeur dans le tableau global. Le
gestionnaire et les threads ont alors accès aux pointeurs de tous les
threads sur la base de leur identifiant. Mais il est peut-être impossible
de lire le tableau de façon async-signal-safe ?

--
LL
Avatar
JKB
Le Fri, 12 Oct 2012 12:17:41 +0200,
Lucas Levrel écrivait :
Le 11 octobre 2012, JKB a écrit :

Le Thu, 11 Oct 2012 17:40:14 +0200,
Lucas Levrel écrivait :
Le 11 octobre 2012, JKB a écrit :

Le Thu, 11 Oct 2012 14:12:55 +0200,
Lucas Levrel écrivait :
Et un « bête » tableau global (à chaque thread sa case) ? Je suppose que
le nombre de threads est variable... Une liste chaînée ?



pthread_self() n'est pas async signal safe. J'y avais déjà pensé...



Mais quand tu crées un thread, tu peux lui passer un identifiant qui sera
son index dans le tableau, non ?



Oui, mais après ?



Eh bien je ne vois pas pourquoi tu as besoin de pthread_self. Est-ce que
l'identifiant « maison » ne suffit pas ?

Sinon, quand un thread est créé, le créateur lui passe un identifiant
« maison » et récupère son pointeur (1er argument de pthread_create) ; il
met alors la paire identifiant-pointeur dans le tableau global. Le
gestionnaire et les threads ont alors accès aux pointeurs de tous les
threads sur la base de leur identifiant. Mais il est peut-être impossible
de lire le tableau de façon async-signal-safe ?



Ce que je ne vois pas (mais je dois rater un truc dans ton
raisonnement). Dans ton gestionnaire de signal, tu as donc un
tableau comportant une association entre un ID 'maison' et le TID.
Tu ne peux utiliser pthread_self(), mais comment connais-tu dans le
gestionnaire l'ID 'maison' ? Tu ne peux le connaître que grâce à une
variable globale au thread et ces fonctions ne sont pas async sygnal
safe.

Ou je n'ai pas compris...

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
Bruno Ducrot
On 12-10-2012, JKB wrote:

Mouais, je me méfie. Lorsqu'une fonction n'est pas async signal
safe, je ne sais pas pourquoi, mais je n'aime pas trop la coller
dans un gestionnaire de signal...



Certes, mais c'est aussi pourquoi j'ai écrit que c'était un truc bien crade
(et donc à ne réserver qu'en cas d'urgence, avec les #define en fonction
de l'OS qui vont bien).

--
Bruno Ducrot

A quoi ca sert que Ducrot hisse des carcasses ?
1 2 3 4 5