Discussion:
gestion des signaux
(trop ancien pour répondre)
Benoit Izac
2010-04-14 21:41:41 UTC
Permalink
Bonjour,

J'ai un petit problème avec la gestions de signaux en Perl. J'ai écrit
un petit script qui ressemble à ça :

while (1) {
$pid = fork;
if (not defined $pid) {
# erreur
} elsif ($pid == 0) {
une_tache;
} else {
sleep 5;
}
}

Jusqu'ici, ça marche bien. Je souhaite rajouter un petit plus en envoyant
un signal et faire autre chose, ça ressemble à ça :

# TSTP est reçu lorsque je tape ^Z
$SIG{TSTP} = sub { autre_tache; };
# même programme que ci-dessus

autre_tache s'exécute bien lorsque je tape ^Z mais le problème c'est que
une_tache s'exécute aussi. Comment faire en sorte que ^Z puisse juste
exécuter autre_tache sans interférer sur le reste du programme ?

Merci.
--
Benoit Izac
Marc Espie
2010-04-14 22:38:43 UTC
Permalink
Post by Benoit Izac
Bonjour,
J'ai un petit problème avec la gestions de signaux en Perl. J'ai écrit
while (1) {
$pid = fork;
if (not defined $pid) {
# erreur
} elsif ($pid == 0) {
une_tache;
} else {
sleep 5;
}
}
Jusqu'ici, ça marche bien. Je souhaite rajouter un petit plus en envoyant
# TSTP est reçu lorsque je tape ^Z
$SIG{TSTP} = sub { autre_tache; };
# même programme que ci-dessus
autre_tache s'exécute bien lorsque je tape ^Z mais le problème c'est que
une_tache s'exécute aussi. Comment faire en sorte que ^Z puisse juste
exécuter autre_tache sans interférer sur le reste du programme ?
Je ne comprend pas du tout ce que tu essaies de faire, exemple trop simplifie
je crains.

Que ton signal interrompe sleep, c'est parfaitement normal et attendu.

En plus, tu as un fork qui traine. Ca complique encore un peu, vu que tous
les process vont voir le ^Z.
Benoit Izac
2010-04-14 23:14:52 UTC
Permalink
Bonjour,

le 15/04/2010 à 00:38, Marc Espie a écrit dans le message
Post by Marc Espie
Je ne comprend pas du tout ce que tu essaies de faire, exemple trop simplifie
je crains.
Que ton signal interrompe sleep, c'est parfaitement normal et attendu.
En plus, tu as un fork qui traine. Ca complique encore un peu, vu que tous
les process vont voir le ^Z.
Un exemple concret :
% cat test.pl
#!/usr/bin/perl
use strict;
use warnings;
$SIG{TSTP} = sub { print "signal received\n"; };
while (1) {
my $pid = fork;
if (not defined $pid) {
die "fork failed";
} elsif ($pid eq 0) {
print time . "\n";
exit;
} else {
sleep 5;
}
}
% perl test.pl
1271286741
1271286746
^Zsignal received
1271286747
^Zsignal received
1271286748
^C
%

Si je remplace
$SIG{TSTP} = sub { print "signal received\n"; };
par
$SIG{TSTP} = 'IGNORE';
^Z n'a aucun effet. Ce que je souhaiterai, c'est la combinaison des
deux : ^Z lance l'exécution de « print "signal received\n"; » mais est
ignoré par le reste du programme.
--
Benoit Izac
Marc Espie
2010-04-15 07:34:42 UTC
Permalink
Post by Benoit Izac
Si je remplace
$SIG{TSTP} = sub { print "signal received\n"; };
par
$SIG{TSTP} = 'IGNORE';
^Z n'a aucun effet. Ce que je souhaiterai, c'est la combinaison des
deux : ^Z lance l'exécution de « print "signal received\n"; » mais est
ignoré par le reste du programme.
Okay. C'est pas possible. SIGTSTP reste un signal. Et, sur ton systeme, ton
sleep est implemente en utilisant pause, qui va etre interrompu par les
signaux. Pour obtenir le resultat voulu, il faudra donc que tu remplaces
ton sleep par un truc plus complexe, qui note l'heure, qui fasse le sleep,
regarde comment celui-ci s'est fini, et en fasse un 2e si un TSTP est
passe par la... tu as peut-etre envie de regarder du cote de Time::HiRes
pour pouvoir faire des choses precises de ce cote-la.

Dans les cas pratiques que je connais, le fait qu'un signal interrompe
sleep n'est pas genant.

ton exemple jouet ne m'explique toujours pas pourquoi ca te gene pour
l'application que tu envisages. sans savoir ce que tu veux reellement faire,
c'est impossible de te conseiller une architecture plus adaptee...

Nota: tu as aussi une race condition dans ton exemple, mais peu visible vu
la brievete de tes process fils. Si tu tapes ^Z pile-poil au bon moment, tes
deux process vont le voir... et tu auras deux fois "signal received".
Alain Ketterlin
2010-04-15 08:03:50 UTC
Permalink
Post by Marc Espie
Post by Benoit Izac
Si je remplace
$SIG{TSTP} = sub { print "signal received\n"; };
par
$SIG{TSTP} = 'IGNORE';
^Z n'a aucun effet. Ce que je souhaiterai, c'est la combinaison des
deux : ^Z lance l'exécution de « print "signal received\n"; » mais est
ignoré par le reste du programme.
Okay. C'est pas possible. SIGTSTP reste un signal. Et, sur ton systeme, ton
sleep est implemente en utilisant pause, qui va etre interrompu par les
signaux. Pour obtenir le resultat voulu, il faudra donc que tu remplaces
ton sleep par un truc plus complexe, qui note l'heure, qui fasse le sleep,
regarde comment celui-ci s'est fini, et en fasse un 2e si un TSTP est
passe par la...
sleep renvoie tout ce qu'il faut pour pouvoir le relancer s'il est
interrompu.

-- Alain.
Benoit Izac
2010-04-15 08:50:54 UTC
Permalink
Bonjour,

le 15/04/2010 à 10:03, Alain Ketterlin a écrit dans le message
Post by Alain Ketterlin
Post by Marc Espie
Okay. C'est pas possible. SIGTSTP reste un signal. Et, sur ton systeme, ton
sleep est implemente en utilisant pause, qui va etre interrompu par les
signaux. Pour obtenir le resultat voulu, il faudra donc que tu remplaces
ton sleep par un truc plus complexe, qui note l'heure, qui fasse le sleep,
regarde comment celui-ci s'est fini, et en fasse un 2e si un TSTP est
passe par la...
sleep renvoie tout ce qu'il faut pour pouvoir le relancer s'il est
interrompu.
Effectivement, je n'avais jamais remarqué cela.
--
Benoit Izac
Benoit Izac
2010-04-15 08:46:04 UTC
Permalink
Bonjour,

le 15/04/2010 à 09:34, Marc Espie a écrit dans le message
Post by Marc Espie
Post by Benoit Izac
Si je remplace
$SIG{TSTP} = sub { print "signal received\n"; };
par
$SIG{TSTP} = 'IGNORE';
^Z n'a aucun effet. Ce que je souhaiterai, c'est la combinaison des
deux : ^Z lance l'exécution de « print "signal received\n"; » mais est
ignoré par le reste du programme.
Okay. C'est pas possible. SIGTSTP reste un signal. Et, sur ton systeme, ton
sleep est implemente en utilisant pause, qui va etre interrompu par les
signaux. Pour obtenir le resultat voulu, il faudra donc que tu remplaces
ton sleep par un truc plus complexe, qui note l'heure, qui fasse le sleep,
regarde comment celui-ci s'est fini, et en fasse un 2e si un TSTP est
passe par la... tu as peut-etre envie de regarder du cote de Time::HiRes
pour pouvoir faire des choses precises de ce cote-la.
Dans les cas pratiques que je connais, le fait qu'un signal interrompe
sleep n'est pas genant.
ton exemple jouet ne m'explique toujours pas pourquoi ca te gene pour
l'application que tu envisages. sans savoir ce que tu veux reellement faire,
c'est impossible de te conseiller une architecture plus adaptee...
En fait, le fils récupère des données et remplit un fichier RRD et je
veux produire le graphique en envoyant un signal. Ce n'est effectivement
pas dramatique que la base soit mises à jour à deux secondes
d'intervalles au lieu de cinq.

Mon réel problème c'est (était) juste de comprendre pourquoi ça ne fait
pas ce que je souhaite.
Post by Marc Espie
Nota: tu as aussi une race condition dans ton exemple, mais peu visible vu
la brievete de tes process fils. Si tu tapes ^Z pile-poil au bon moment, tes
deux process vont le voir... et tu auras deux fois "signal received".
Là, il me suffit d'ajouter « $SIG{TSTP} = 'IGNORE'; » dans le fils.
--
Benoit Izac
Marc Espie
2010-04-15 09:07:15 UTC
Permalink
Post by Benoit Izac
Post by Marc Espie
Nota: tu as aussi une race condition dans ton exemple, mais peu visible vu
la brievete de tes process fils. Si tu tapes ^Z pile-poil au bon moment, tes
deux process vont le voir... et tu auras deux fois "signal received".
Là, il me suffit d'ajouter « $SIG{TSTP} = 'IGNORE'; » dans le fils.
Non, tu vas reduire la race condition, mais pas la supprimer ;-)
entre le fork() et le $SIG{TSTP} = 'IGNORE', tu as toujours la race
condition.

La seule vraie facon de corriger ca pour de bon, c'est de bloquer/debloquer
les signaux autour du fork. Confere POSIX.
Benoit Izac
2010-04-15 09:56:39 UTC
Permalink
Bonjour,

le 15/04/2010 à 11:07, Marc Espie a écrit dans le message
Post by Marc Espie
Post by Benoit Izac
Post by Marc Espie
Nota: tu as aussi une race condition dans ton exemple, mais peu
visible vu la brievete de tes process fils. Si tu tapes ^Z pile-poil
au bon moment, tes deux process vont le voir... et tu auras deux
fois "signal received".
Là, il me suffit d'ajouter « $SIG{TSTP} = 'IGNORE'; » dans le fils.
Non, tu vas reduire la race condition, mais pas la supprimer ;-)
entre le fork() et le $SIG{TSTP} = 'IGNORE', tu as toujours la race
condition.
La seule vraie facon de corriger ca pour de bon, c'est de
bloquer/debloquer les signaux autour du fork. Confere POSIX.
Tu as raison. Je vais arrêter de bidouiller et ouvrir mon Stevens&Rago.
--
Benoit Izac
Alain Ketterlin
2010-04-15 09:58:01 UTC
Permalink
Post by Benoit Izac
En fait, le fils récupère des données et remplit un fichier RRD et je
veux produire le graphique en envoyant un signal. Ce n'est effectivement
pas dramatique que la base soit mises à jour à deux secondes
d'intervalles au lieu de cinq.
Juste au cas où... : t'es sûr que le signal est la bonne solution ? Ton
fils risque d'écraser le fichier avant que le père l'ait lu complètement
(théoriquement au moins). Un bête pipe te fournirait la communication et
la synchronisation.

-- Alain.
Benoit Izac
2010-04-15 10:22:30 UTC
Permalink
Bonjour,

le 15/04/2010 à 11:58, Alain Ketterlin a écrit dans le message
Post by Alain Ketterlin
Post by Benoit Izac
En fait, le fils récupère des données et remplit un fichier RRD et je
veux produire le graphique en envoyant un signal. Ce n'est effectivement
pas dramatique que la base soit mises à jour à deux secondes
d'intervalles au lieu de cinq.
Juste au cas où... : t'es sûr que le signal est la bonne solution ?
Absolument pas, c'est juste ce qui la première idée que j'ai eu en
écrivant le code et je n'ai vraiment pas réfléchi à tous les problèmes
que ça pouvait engendrer. Mais comme dit le proverbe, « c'est en
forgeant que l'on devient forgeron ».
Post by Alain Ketterlin
Ton fils risque d'écraser le fichier avant que le père l'ait lu
complètement (théoriquement au moins). Un bête pipe te fournirait la
communication et la synchronisation.
Là, je ne sais pas comment se comporte rrdtool si un RRDs::update se
fait en même temps qu'un RRDs::graph ; j'ose espéré que ça a été conçu
pour mais il faudra quand même que je jette oeil dessus.

Bon, après, il faut quand même dire que ce code n'est pas de très grande
importance. Il n'est pas destiné à être diffusé et ne doit servir qu'à
monitorer la charge/décharge de ma batterie qui me semble plus que
fatiguée.

Le but second étant de me remettre à Perl que j'ai un peu délaissé ces
derniers temps. Je me rends compte au fur et à mesure que dès qu'on sort
des sentiers battus, ça demande des connaissances sur beaucoup de choses
et j'espérais que Perl s'en occuperait pour moi. Je crois qu'il est
temps que je me plonge dans tous les parties avancées de la
programmation système que j'avais jusqu'ici laissées de coté.
--
Benoit Izac
Benoit Izac
2010-04-15 20:44:37 UTC
Permalink
Grâce à vos remarque, Je crois que je suis arrivé à faire ce que je
voulais assez simplement. Je ne suis pas sûr pour la race condition mais
ça à l'air de fonctionner au poil. Commentaires bienvenus !

#!/usr/bin/perl
use strict;
use warnings;

while (1) {
$SIG{TSTP} = 'IGNORE'; # merci Marc !
my $pid = fork;
if (not defined $pid) {
die "fork failed";
} elsif ($pid eq 0) {
print time . "\n";
exit;
} else {
$SIG{TSTP} = sub { print "signal received\n"; };
my $t = 5;
do { $t -= sleep $t } while ($t); # merci Alain !
}
}
--
Benoit Izac
Denis Dordoigne
2010-04-16 05:02:01 UTC
Permalink
Bonjour,
Post by Benoit Izac
do { $t -= sleep $t } while ($t);
Je ne sais pas quel est ton OS, mais connaissant les surprises que l'on
peut avoir parfois avec la gestion des secondes, j'écrirais plutôt :
do { $t -= sleep $t } while ($t >= 0);

Sinon vu l'usage prévu il est improbable que 2 signaux soient reçus en 5
secondes, si c'était du code jetable j'écrirais carrément :
sleep($t - sleep $t);

Bonne continuation,
--
Denis Dordoigne
Membre de l'April - promouvoir et défendre le logiciel libre - april.org
Rejoignez maintenant plus de 5 000 personnes, associations,
entreprises et collectivités qui soutiennent notre action
Benoit Izac
2010-04-16 09:48:15 UTC
Permalink
Bonjour,

le 16/04/2010 à 07:02, Denis Dordoigne a écrit dans le message
Post by Denis Dordoigne
Post by Benoit Izac
do { $t -= sleep $t } while ($t);
Je ne sais pas quel est ton OS
Là c'est un Linux, mais ça pourrait-être n'importe quel OS de type Unix.
Post by Denis Dordoigne
mais connaissant les surprises que l'on peut avoir parfois avec la
do { $t -= sleep $t } while ($t >= 0);
Je ne pense pas que ce soit nécessaire sur ma plateforme. Existe-t-il un
équivalent à assert(3) en Perl ?
Post by Denis Dordoigne
Sinon vu l'usage prévu il est improbable que 2 signaux soient reçus en
sleep($t - sleep $t);
Oui mais là ça coûte pas bien cher de le faire plus ou moins
correctement. Il y aurait plusieurs dizaines de ligne de code je dis pas.
--
Benoit Izac
Benoit Izac
2010-04-16 09:43:02 UTC
Permalink
Post by Benoit Izac
Grâce à vos remarque, Je crois que je suis arrivé à faire ce que je
voulais assez simplement. Je ne suis pas sûr pour la race condition mais
ça à l'air de fonctionner au poil. Commentaires bienvenus !
#!/usr/bin/perl
use strict;
use warnings;
while (1) {
$SIG{TSTP} = 'IGNORE'; # merci Marc !
my $pid = fork;
if (not defined $pid) {
die "fork failed";
} elsif ($pid eq 0) {
print time . "\n";
exit;
} else {
$SIG{TSTP} = sub { print "signal received\n"; };
my $t = 5;
do { $t -= sleep $t } while ($t); # merci Alain !
}
}
Bon il y a quand même un petit problème avec le code ci-dessus : c'est
la fête des zombies !

Quelle est la meilleure façon de gérer les fils ?
* $SIG{CHLD} = 'IGNORE'; après le while (1)
* use POSIX ":sys_wait_h"; while (waitpid(-1, WNOHANG) > 0) {}
après le sleep (ou ailleurs ?)
--
Benoit Izac
Marc Espie
2010-04-16 14:13:36 UTC
Permalink
Post by Benoit Izac
Bon il y a quand même un petit problème avec le code ci-dessus : c'est
la fête des zombies !
Quelle est la meilleure façon de gérer les fils ?
* $SIG{CHLD} = 'IGNORE'; après le while (1)
* use POSIX ":sys_wait_h"; while (waitpid(-1, WNOHANG) > 0) {}
après le sleep (ou ailleurs ?)
il n'y a pas de meilleure facon. Si les deux sont possibles, c'est parce
qu'elles ont un usage different.

- si tu n'en as jamais rien a battre du statut de tes fils, alors
indubitablement, IGNORE sigchld.
- si au contraire tu veux t'assurer que tout s'est bien passe, il faut
au minimum enregistrer les pid dans un hash, faire le wait qui va bien,
et prendre une action appropriee.

De toutes facons, depuis le debut de ce thread, tes problemes ne sont
pas specifiques a perl. C'est entierement des soucis de design
d'application Unix...

Dans mon experience, il y a de bonnes chances que la vraie solution soit
de mettre tes besoins a plat, de reflechir a une architecture la plus simple
possible, et de reecrire une bonne partie de ton code.

Loading...