OVH Cloud OVH Cloud

Ecriture dans une FIFO

6 réponses
Avatar
Yves Martin
Bonjour,

Je cherche à écrire un producteur de contenu dans un fichier FIFO:
mkfifo decision

#!/usr/bin/perl -Tw

use FileHandle;

my $fh = new FileHandle("decision", "w");

sub value {
print "Evaluation !\n";
return 5;
}

while (1) {
$fh->print(&value() . "\n\004");
}

Lorsque je lance le processus, et que je fais 'cat decision', je m'attends à
ce qu'une seule ligne sorte et que 'cat' termine sur EOF (\004 mais c'est
peut-être une erreur ?).

Actuellement ce code boucle à l'infini, 'cat' ne se termine jamais.

Une solution serait d'ouvrir et fermer le fichier à chaque ouverture par un
consommateur mais je me pose des questions de performance et si possible
j'aimerai conserver le descripteur de fichier ouvert.

Savez-vous ce qui ne fonctionne pas dans mon code ? Comment sortir un 'EOF'
sur le FIFO sans effectivement fermer le fichier ?
Merci d'avance pour votre aide
--
Yves Martin

6 réponses

Avatar
Stephane Chazelas
2005-01-13, 10:46(+01), Yves Martin:
[...]
while (1) {
$fh->print(&value() . "n04");
}

Lorsque je lance le processus, et que je fais 'cat decision', je m'attends à
ce qu'une seule ligne sorte et que 'cat' termine sur EOF (04 mais c'est
peut-être une erreur ?).


Oui, 4 n'est le charactere d'eof que pour un terminal (a moins
qu'on ne le change par un stty eof 'autre-character').

Il faut fermer le handle coté perl pour que cat voie un eof.

--
Stephane

Avatar
Yves Martin
Stephane Chazelas writes:

2005-01-13, 10:46(+01), Yves Martin:
[...]
while (1) {
$fh->print(&value() . "n04");
}

Lorsque je lance le processus, et que je fais 'cat decision', je m'attends
à ce qu'une seule ligne sorte et que 'cat' termine sur EOF (04 mais c'est
peut-être une erreur ?).


Oui, 4 n'est le charactere d'eof que pour un terminal (a moins
qu'on ne le change par un stty eof 'autre-character').

Il faut fermer le handle coté perl pour que cat voie un eof.


C'est ce que j'ai fait mais ce n'est visiblement pas suffisant.

#!/usr/bin/perl -Tw

use FileHandle;

my $fh = new FileHandle();

while (1) {
$fh->open("decision", "w");
$fh->print("5n");
$fh->close();
## sleep(1);
}

Maintenant cela donne un nombre de lignes aléatoires dans 'cat' !!!

yma ~/perl> cat decision | wc
286 286 572
yma ~/perl> cat decision | wc
461 461 922
yma ~/perl> cat decision | wc
127 127 254
yma ~/perl> cat decision | wc
143 143 286

En rajoutant 'sleep(1)', le résultat mais juste mais ce n'est pas la
réactivité à laquelle je m'attendais... Est-ce que l'on peut mieux faire ? Et
comment ?

Je pense à:
- un sleep qui serait de qq microsecondes ?
- un flush avant le close ?
- forcer le changement de contexte après le close ?

Merci pour votre aide
--
Yves Martin


Avatar
Stephane Chazelas
2005-01-13, 11:47(+01), Yves Martin:
[...]
#!/usr/bin/perl -Tw

use FileHandle;

my $fh = new FileHandle();

while (1) {
$fh->open("decision", "w");
$fh->print("5n");
$fh->close();
## sleep(1);
}

Maintenant cela donne un nombre de lignes aléatoires dans 'cat' !!!

yma ~/perl> cat decision | wc
286 286 572


Le probleme survient quand perl a le temps de faire le print le
close et le 2e open entre le moment ou cat fait son open et son
read.

Il faudrait que perl attende que cat ait eu son eof avant de
reouvrir le fichier. Une alternative est de recreer un nouveau
pipe.

#!/usr/bin/perl -w

use FileHandle;
use POSIX;

my $fh = new FileHandle();

mkfifo("decision", 0600);
while (1) {
$fh->open("decision", "w");
unlink("decision");
mkfifo("decision", 0600);
$fh->print("5n");
$fh->close();
## sleep(1);
}


--
Stephane

Avatar
Yves Martin
Stephane Chazelas writes:

Le probleme survient quand perl a le temps de faire le print le
close et le 2e open entre le moment ou cat fait son open et son
read.


Etrange. Je n'ai pas ce genre de problème avec un shell script si je me
souviens bien. Mais il est vrai qu'un processus est forké à chaque demande.

while( true ); do; fortune > fifo; done

Il faudrait que perl attende que cat ait eu son eof avant de
reouvrir le fichier. Une alternative est de recreer un nouveau
pipe.

#!/usr/bin/perl -w

use FileHandle;
use POSIX;

my $fh = new FileHandle();

mkfifo("decision", 0600);
while (1) {
$fh->open("decision", "w");
unlink("decision");
mkfifo("decision", 0600);
$fh->print("5n");
$fh->close();
## sleep(1);
}


Je ne dis pas que cela ne fonctionne pas mais:

- est-on sûr de ne "rien" perdre ?
Mon fifo doit fournir une et une seule ligne à un autre processus (bien sur
'cat' est simplement utilisé pour mes tests)

- est-ce que la gestion des 'inode' sur le filesystem ne va pas exploser ?

- ne serait-il pas mieux d'utiliser un autre mécanisme qu'un fifo pour assurer
la qualité de communication ? (je pense à une socket Unix ou autres IPC)

Merci pour ton aide précieuse
--
Yves Martin

Avatar
Stephane Chazelas
2005-01-13, 14:14(+01), Yves Martin:
Stephane Chazelas writes:

Le probleme survient quand perl a le temps de faire le print le
close et le 2e open entre le moment ou cat fait son open et son
read.


Etrange. Je n'ai pas ce genre de problème avec un shell script si je me
souviens bien. Mais il est vrai qu'un processus est forké à chaque demande.

while( true ); do; fortune > fifo; done


Oui, avec le fork et la lenteur generale des shell, ca limite la
probabilité que ca arrive.

Tu peux faire dans 2 shells differents (A et B):

A$ exec 3> decision
B$ exec 3< decision
A$ echo toto >&3
A$ exec 3>&- # (close(3))
A$ exec 3> decision
A$ echo titi >&3
B$ cat <&3

Tu verras que le cat <&3 renvoie le toto et le titi.

[...]
#!/usr/bin/perl -w

use FileHandle;
use POSIX;

my $fh = new FileHandle();

mkfifo("decision", 0600);
while (1) {
$fh->open("decision", "w");
unlink("decision");
mkfifo("decision", 0600);
$fh->print("5n");
$fh->close();
## sleep(1);
}


Je ne dis pas que cela ne fonctionne pas mais:

- est-on sûr de ne "rien" perdre ?


Oui a priori, a moins que perl ou cat plantent au milieu de leur
ecriture/lecture.


Mon fifo doit fournir une et une seule ligne à un autre processus (bien sur
'cat' est simplement utilisé pour mes tests)


Alors pas la peine de fermer le pipe, lit juste une ligne a la
fois (en lisant un caractere a la fois si plusieurs processus
doivent lire sur le fifo).

Un autre solution est de faire repondre le deuxieme processus
(cat) pour une meilleur synchronisation.

- est-ce que la gestion des 'inode' sur le filesystem ne va pas exploser ?


Non, pourquoi.

- ne serait-il pas mieux d'utiliser un autre mécanisme qu'un fifo pour assurer
la qualité de communication ? (je pense à une socket Unix ou autres IPC)


Oui, les sockets sont plus flexible, surtout s'il y a plus de 2
process.

--
Stephane


Avatar
Yves Martin
Stephane Chazelas writes:

Mon fifo doit fournir une et une seule ligne à un autre processus (bien
sur 'cat' est simplement utilisé pour mes tests)


Alors pas la peine de fermer le pipe, lit juste une ligne a la
fois (en lisant un caractere a la fois si plusieurs processus
doivent lire sur le fifo).


Effectivement, je n'avais pas pensé à cela.

- est-ce que la gestion des 'inode' sur le filesystem ne va pas exploser ?


Non, pourquoi.


Parce que faire des unlink / mkfifo en permanence ne me semble pas naturel.

- ne serait-il pas mieux d'utiliser un autre mécanisme qu'un fifo pour
assurer la qualité de communication ? (je pense à une socket Unix ou autres
IPC)


Oui, les sockets sont plus flexible, surtout s'il y a plus de 2
process.


Comme c'est un script PHP (donc éventuellement plusieurs processus) qui vont
lire le résultat de mon évaluation en Perl, il me faut une solution robuste.

Donc j'ai tout implémenté sur une socket TCP avec un basic protocol
d'interrogation.

Finallement les FIFO c'est facile et pas cher mais il ne faut pas trop leur en
demander.

Merci pour ton aide.
--
Yves Martin