OVH Cloud OVH Cloud

pipe et fork...

15 réponses
Avatar
JKB
Bonjour à tous,

Je suis en train de couper un code assez gros avec un fork pour le
faire tourner sur des machines multipro (je ne peux pas le
multithreader). Pour faire communiquer les deux parties, j'aimerais
utiliser pipe(), mais une question me taraude : pour fermer le pipe,
je vais utiliser close(), mais dois-je utiliser close() dans le fils
seul (est-ce suffisant) ou dans le fils _et_ le père ? La page man
est muette et je n'ai les idées très claires...

Merci de vos lumières,

JKB

10 réponses

1 2
Avatar
Stephane Chazelas
2004-12-15, 11:08(+00), JKB:
Bonjour à tous,

Je suis en train de couper un code assez gros avec un fork pour le
faire tourner sur des machines multipro (je ne peux pas le
multithreader). Pour faire communiquer les deux parties, j'aimerais
utiliser pipe(), mais une question me taraude : pour fermer le pipe,
je vais utiliser close(), mais dois-je utiliser close() dans le fils
seul (est-ce suffisant) ou dans le fils _et_ le père ? La page man
est muette et je n'ai les idées très claires...
[...]


Si tu close cote gauche (ecriture), le coté droit (lecture)
verra une eof et tu peux dire au cote droit de fermer sur
eof. Ca ne sert a rien de ne pas fermer ce fd. Ca libere des
resources de le fermer.

Si tu commences par fermer le cote droit (lecture), alors le
coté gauche (ecriture) se prendra un SIGPIPE s'il essaie
d'ecrire, ce n'est peut-etre pas ce que tu veux.

--
Stephane

Avatar
JKB
Le 15-12-2004, à propos de
Re: pipe et fork...,
Stephane Chazelas écrivait dans fr.comp.os.unix :
2004-12-15, 11:08(+00), JKB:
Bonjour à tous,

Je suis en train de couper un code assez gros avec un fork pour le
faire tourner sur des machines multipro (je ne peux pas le
multithreader). Pour faire communiquer les deux parties, j'aimerais
utiliser pipe(), mais une question me taraude : pour fermer le pipe,
je vais utiliser close(), mais dois-je utiliser close() dans le fils
seul (est-ce suffisant) ou dans le fils _et_ le père ? La page man
est muette et je n'ai les idées très claires...
[...]


Si tu close cote gauche (ecriture), le coté droit (lecture)
verra une eof et tu peux dire au cote droit de fermer sur
eof. Ca ne sert a rien de ne pas fermer ce fd. Ca libere des
resources de le fermer.

Si tu commences par fermer le cote droit (lecture), alors le
coté gauche (ecriture) se prendra un SIGPIPE s'il essaie
d'ecrire, ce n'est peut-etre pas ce que tu veux.


Je ne comprends pas bien...
Plus pratiquement, je vais faire un

pipe(fp);

if (pid = fork())
{
// père
}
else
{
// fils
close(fp[0]);
close(fp[1]);

exit(0);
}

Ma question est simplement : est-ce que les deux close() dans le
fils suffisent ou dois-je aussi mettre deux close() dans le père ?

JKB


Avatar
Christian CAMIER
JKB wrote:
Le 15-12-2004, à propos de
Re: pipe et fork...,
Stephane Chazelas écrivait dans fr.comp.os.unix :


Je ne comprends pas bien...
Plus pratiquement, je vais faire un

pipe(fp);

if (pid = fork())
{
// père
}
else
{
// fils
close(fp[0]);
close(fp[1]);

exit(0);
}

Ma question est simplement : est-ce que les deux close() dans le
fils suffisent ou dois-je aussi mettre deux close() dans le père ?

JKB


dans ton exemple, ton pipe n'est utile que pour la communication du pere
avec lui même. Un pipe est composé ainsi :
fp[1] -> === -> fp[0]
Tu lis avec fp[0] ce qui est écrit avec fp[1]. Il te faut donc, dans
chaque processus, fermer le descripteur inutil.
Prenons l'exemple ou le père consomme les données (il lit dans le pipe),
et le fils produit les données (il écrit dans le pipe), tu obtiens un
code come suit :

if(pipe(fp))
{
perror("pipe");
exit(1);
}
switch(pid = fork())
{
case -1: /* Erreur */
perror("fork");
exit(1);

case 0: /* Fils */
close(fp[0]); /* Inutil ici */
write(fp[1], "Message", sizeof("Message") - 1);
exit(0);

default: /* Pere */
close(fp[1]); /* Si pas fermé, pas de EOF à la mort du fils */
while((n = read(fp[0], buff, sizeof(buff))) > 0)
write(1, buff, n);
write(1, "n", 1);
exit(0);
}

Maintenant si tu désires une communication bidirectionnelle, il te
faudra créer 2 pipes.

fp1[0] <- ===p1=== <- fp1[1]
pere fils
fp2[1] -> ===p2=== -> fp2[0]


Si malgré tout, tu n'est toujours pas à l'aise et que ta comm est
unidirectionnelle, tu peux toujours découper ton programme en deux
programmes distincts, un qui produit sur la sortuie standard (prod) et
un qui consomme sur l'entrée standard (cons).
Pour lancer le tout, tu crée un script shell (prog) ainsi :

#!/bin/sh
prod | cons

Avatar
FAb
JKB writes:

Bonjour à tous,

Je suis en train de couper un code assez gros avec un fork pour le
faire tourner sur des machines multipro (je ne peux pas le


Simple question, es-tu sûr d'y gagner ? Sur certains OS, les familles de
processus reste sur le même processeurs il me semble. (ou alors c'est
paramétrable mais pas en userlambda). Du coup tu introduirais des coups de
changements de contextes...
Je me doute que tu as la réponse mais j'espère que les cadors du coin en
profiterons pour partager leur savoir et nous ne dire un peu plus.

Merci de vos lumières,


Perdu...

FAb

Avatar
JKB
Le 15-12-2004, à propos de
Re: pipe et fork...,
FAb écrivait dans fr.comp.os.unix :
JKB writes:

Bonjour à tous,

Je suis en train de couper un code assez gros avec un fork pour le
faire tourner sur des machines multipro (je ne peux pas le


Simple question, es-tu sûr d'y gagner ? Sur certains OS, les familles de
processus reste sur le même processeurs il me semble. (ou alors c'est
paramétrable mais pas en userlambda). Du coup tu introduirais des coups de
changements de contextes...
Je me doute que tu as la réponse mais j'espère que les cadors du coin en
profiterons pour partager leur savoir et nous ne dire un peu plus.

Merci de vos lumières,


Perdu...


En fait, mon problème est simple. Je bosse depuis très longtemps sur
l'écriture d'un langage de programmation destiné à scripter des gros
calculs (j'ai des simulations de propagation qui tournent dessus
plusieurs semaines). Aujourd'hui, je suis devant des machines de
calcul à plusieurs processeurs et j'aimerais rajouter un truc du
genre :

'ma_routine' detach -> lance 'ma_routine' sur un second processus,
mon programme 'appelant' continuant jusqu'àce qu'il rencontre un
wfchild (wait for child) qui récupère les données du processus fils.
Je ne peux pas faire le truc en multithreadé pour des raisons de
partage de la mémoire et des piles.

Il est sûr que si le fils s'exécute sur le même processeur sachant
qu'un second à côté ne fiche rien n'est pas exactement ce que je
recherche.

Cordialement,

JKB


Avatar
JKB
Le 15-12-2004, à propos de
Re: pipe et fork...,
Christian CAMIER écrivait dans fr.comp.os.unix :
JKB wrote:
Le 15-12-2004, à propos de
Re: pipe et fork...,
Stephane Chazelas écrivait dans fr.comp.os.unix :


Je ne comprends pas bien...
Plus pratiquement, je vais faire un

pipe(fp);

if (pid = fork())
{
// père
}
else
{
// fils
close(fp[0]);
close(fp[1]);

exit(0);
}

Ma question est simplement : est-ce que les deux close() dans le
fils suffisent ou dois-je aussi mettre deux close() dans le père ?

JKB


dans ton exemple, ton pipe n'est utile que pour la communication du pere
avec lui même. Un pipe est composé ainsi :
fp[1] -> === -> fp[0]
Tu lis avec fp[0] ce qui est écrit avec fp[1]. Il te faut donc, dans
chaque processus, fermer le descripteur inutil.
Prenons l'exemple ou le père consomme les données (il lit dans le pipe),
et le fils produit les données (il écrit dans le pipe), tu obtiens un
code come suit :

if(pipe(fp))
{
perror("pipe");
exit(1);
}
switch(pid = fork())
{
case -1: /* Erreur */
perror("fork");
exit(1);

case 0: /* Fils */
close(fp[0]); /* Inutil ici */
write(fp[1], "Message", sizeof("Message") - 1);


Est-ce que j'ai tout faut en rajoutant :
close(fp[1]);

exit(0);

default: /* Pere */
close(fp[1]); /* Si pas fermé, pas de EOF à la mort du fils */
while((n = read(fp[0], buff, sizeof(buff))) > 0)
write(1, buff, n);
write(1, "n", 1);


close(fp[0]);

exit(0);
}


JKB


Avatar
Thomas Labourdette
JKB a écrit le mercredi 15 Décembre 2004 14:23 :

Le 15-12-2004, à propos de
Re: pipe et fork...,
Christian CAMIER écrivait dans fr.comp.os.unix :
case 0: /* Fils */
close(fp[0]); /* Inutil ici */
write(fp[1], "Message", sizeof("Message") - 1);


Est-ce que j'ai tout faut en rajoutant :
close(fp[1]);


Non tu as raison

exit(0);

default: /* Pere */
close(fp[1]); /* Si pas fermé, pas de EOF à la mort du fils */
while((n = read(fp[0], buff, sizeof(buff))) > 0)
write(1, buff, n);
write(1, "n", 1);


close(fp[0]);


Oui

@+
--
Mélodie MATAZÉRO (signature aléatoire)
Pourquoi les femmes ne peuvent se mettre du mascara la bouche fermée ?


Avatar
Vincent Bernat
OoO Pendant le temps de midi du mercredi 15 décembre 2004, vers 12:08,
JKB disait:

Je suis en train de couper un code assez gros avec un fork pour le
faire tourner sur des machines multipro (je ne peux pas le
multithreader). Pour faire communiquer les deux parties, j'aimerais
utiliser pipe(), mais une question me taraude : pour fermer le pipe,
je vais utiliser close(), mais dois-je utiliser close() dans le fils
seul (est-ce suffisant) ou dans le fils _et_ le père ? La page man
est muette et je n'ai les idées très claires...


Quand tu forkes, les descripteurs se retrouvent chacun en double : un
pour le fils, un pour le père. Si tu veux les fermer proprement, il
faudra donc fermer l'exemplaire du fils et l'exemplaire du père. Un
descripteur sera considéré comme fermé que lorsque les deux
exemplaires seront fermés. Donc ton pipe n'aura plus de lecteur quand
les deux descripteurs lecteurs seront fermés.
--
printk(KERN_WARNING "Warning: defective CD-ROM (volume sequence
number). Enabling "cruft" mount option.n");
2.2.16 /usr/src/linux/fs/isofs/inode.c

Avatar
Nicolas George
JKB wrote in message :
Je suis en train de couper un code assez gros avec un fork pour le
faire tourner sur des machines multipro (je ne peux pas le
multithreader). Pour faire communiquer les deux parties, j'aimerais
utiliser pipe(), mais une question me taraude : pour fermer le pipe,
je vais utiliser close(), mais dois-je utiliser close() dans le fils
seul (est-ce suffisant) ou dans le fils _et_ le père ? La page man
est muette et je n'ai les idées très claires...


Pour compléter les réponses des autres, un petit dessin, ça aide à
comprendre :

Après le pipe(), on a ceci :

+-------------+
| |
| père |
| |
| fd[1] ----
| |
| |
| | |
| | v
| | |
| | /
| | /
| fd[0]<----
| |
+-------------+

Après le fork, le prcessus est dupliqué, ses file-descriptors aussi, mais
pas le pipe lui-même, donc ça ressemble à ça :

+-------------+ +-------------+
| | | |
| père | | fils |
| | | |
| fd[1] ---- ---- fd[1] |
| | / | |
| | / | |
| | | | |
| | v | |
| | | | |
| | / | |
| | / | |
| fd[0]<---- ---->fd[0] |
| | | |
+-------------+ +-------------+

Pour la suite des opérations, il faut savoir ce qu'on veut. Si le but est
d'avoir un canal qui va du père vers le fils, on veut quelque chose comme
ça :

+-------------+ +-------------+
| | | |
| père | | fils |
| | | |
| fd[1] ---- | |
| | | |
| | | |
| | | | |
| | v | |
| | | | |
| | | |
| | | |
| | ---->fd[0] |
| | | |
+-------------+ +-------------+

On va donc fermer fd[0] dans le père, et fd[1] dans le fils. Si on veut un
canal du fils vers le père, il faut faire l'inverse.

Si on veut un canal dans les deux sens, il faut faire en fait deux canaux,
un dans chaque sens, car les pipes ne sont pas portablement full-duplex.

Avatar
Pascal Bourguignon
Quand même, l'ASCII-Art a du bon!

Nicolas George <nicolas$ writes:
Pour compléter les réponses des autres, un petit dessin, ça aide à
comprendre :

Après le pipe(), on a ceci :

+-------------+
| |
| père |
| |
| fd[1] ----
| |
| |
| | |
| | v
| | |
| | /
| | /
| fd[0]<----
| |
+-------------+

Après le fork, le prcessus est dupliqué, ses file-descriptors aussi, mais
pas le pipe lui-même, donc ça ressemble à ça :

+-------------+ +-------------+
| | | |
| père | | fils |
| | | |
| fd[1] ---- ---- fd[1] |
| | / | |
| | / | |
| | | | |
| | v | |
| | | | |
| | / | |
| | / | |
| fd[0]<---- ---->fd[0] |
| | | |
+-------------+ +-------------+

Pour la suite des opérations, il faut savoir ce qu'on veut. Si le but est
d'avoir un canal qui va du père vers le fils, on veut quelque chose comme
ça :

+-------------+ +-------------+
| | | |
| père | | fils |
| | | |
| fd[1] ---- | |
| | | |
| | | |
| | | | |
| | v | |
| | | | |
| | | |
| | | |
| | ---->fd[0] |
| | | |
+-------------+ +-------------+

On va donc fermer fd[0] dans le père, et fd[1] dans le fils. Si on veut un
canal du fils vers le père, il faut faire l'inverse.

Si on veut un canal dans les deux sens, il faut faire en fait deux canaux,
un dans chaque sens, car les pipes ne sont pas portablement full-duplex.


--
__Pascal Bourguignon__ http://www.informatimago.com/
Cats meow out of angst
"Thumbs! If only we had thumbs!
We could break so much!"

1 2