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

stdin, stdout, pipe (en C)

5 réponses
Avatar
un.gabacho.sans.pourrier
Salut,

je pense que je suis au bon endroit pour poser la question, si c'est
pas le cas, pas taper, pas crier, moi pas fait exprès.

Je peux lancer un processus depuis un programme C avec system(), vrai ?

Ce que je voudrais faire c'est dialoguer avec lui à travers son entrée
et sa sortie standard, genre je lui fait un fputs, il envoie des
choses que je lis avec des fgetc (jusqu'à un EOF je suppose ?), etc.

Comment c'est-i que je m'y prends ?

5 réponses

Avatar
DINH Viêt Hoà

Je peux lancer un processus depuis un programme C avec system(), vrai ?


ou via exec() et compagnie.

Ce que je voudrais faire c'est dialoguer avec lui à travers son entrée
et sa sortie standard, genre je lui fait un fputs, il envoie des
choses que je lis avec des fgetc (jusqu'à un EOF je suppose ?), etc.


attention, les fonctions du C standard [f]printf(), etc, sont bufferisées,
donc il faudra voir un peu du côté de fflush() pour que les données
passent bien d'un processus à un autre.

Sinon, il faut voir du côté de pipe(), dup2(), fork() pour rediriger les
entrées et sorties standard.

Petit exemple :

{
int r;
int fd_child_to_parent[2];
int fd_parent_to_child[2];

r = pipe(fd_child_to_parent)
if (r != -1) {
exit(EXIT_FAILURE);
}
r = pipe(fd_parent_to_child)
if (r != -1) {
exit(EXIT_FAILURE);
}

r = fork();
if (r == -1) {
exit(EXIT_FAILURE);
}
if (r == 0) {
/* processus fils */

r = dup2(fd_child_to_parent[1], 1);
if (r < 0) {
exit(EXIT_FAILURE);
}
close(fd_child_to_parent[1]);
close(fd_child_to_parent[0]);

r = dup2(fd_parent_to_child[0], 0);
if (r < 0) {
exit(EXIT_FAILURE);
}
close(fd_parent_to_child[0]);
close(fd_parent_to_child[1]);

execl("programme", "programme", "arg1", "arg2", NULL);
}
else {
char buf[256];

close(fd_parent_to_child[0]);

/* processus père */
r = write(fd_parent_to_child[1], "toto", 4);
if (r < 0) {
exit(EXIT_FAILURE);
}

close(fd_child_to_parent[0]);

r = read(fd_child_to_parent[0], buf, sizeof(buf));
if (r < 0) {
exit(EXIT_FAILURE);
}
...
}
}


--
DINH V. Hoa,

"monde de merde" -- Erwan David

Avatar
Stephane Chazelas
2003/11/01, 15:57(+01), :
[...]
Je peux lancer un processus depuis un programme C avec system(), vrai ?

Ce que je voudrais faire c'est dialoguer avec lui à travers son entrée
et sa sortie standard, genre je lui fait un fputs, il envoie des
choses que je lis avec des fgetc (jusqu'à un EOF je suppose ?), etc.


2 pipes, un pour l'entree standard, l'autre pour la sortie
standard, tu exécutes le programme dans un fils. Tu fais des
fdopen (et des setbuf) sur les file descriptors coté père pour
pouvoir utiliser les fonctions stdio. Dans le fils, avant
d'executer le programme, tu rediriges les entree et sortie
standard vers les pipe (dup2).

Voir ces pages de man: pipe, fork, fdopen, setbuf, dup2, close,
waitpid, kill.

Sinon, tu peux peut-être te contenter d'un
popen("echo toto | cmd", "r")
si tu es pressé.

--
Stéphane ["Stephane.Chazelas" arobase "free.fr"]

Avatar
un.gabacho.sans.pourrier
DINH Viêt Hoà a répondu :

Je peux lancer un processus depuis un programme C avec system(), vrai ?


ou via exec() et compagnie.


Euh oui. C'est sans doute plus approprié.

Ce que je voudrais faire c'est dialoguer avec lui à travers son entrée
et sa sortie standard, genre je lui fait un fputs, il envoie des
choses que je lis avec des fgetc (jusqu'à un EOF je suppose ?), etc.


attention, les fonctions du C standard [f]printf(), etc, sont bufferisées,
donc il faudra voir un peu du côté de fflush() pour que les données
passent bien d'un processus à un autre.


Ça d'accord, je comprends.

Sinon, il faut voir du côté de pipe(), dup2(), fork() pour rediriger les
entrées et sorties standard.


Ouah.
Je ne connaissais que fork(), et la lecture de son man ne m'a pas suffit.
Je trouve que les « see also » du man standard sont un peu légers.
Faut que je me trouve un petit tutorial de programmation sous unix.

Il est vrai que j'aurais pu penser tout seul à faire man pipe... mais
euh vu la suite ça ne m'aurait pas suffit.

Alors je vais poser des tas de questions. Je ferais mieux de faire des
essais, mais bon, voilà, j'ai eu comme premier réflexe de lire le code
plutôt que le compiler, et je l'ai commenté au fil de la lecture. Je
pense qu'au final je comprends, mais si tu peux répondre à mes « c'est
ça ? » et autres « j'ai bon ? » ma foi...

Mais tant que j'ai pas fait d'essais tu n'as aucune raison de te
fatiguer, à vrai dire.

Petit exemple :

{
int r;
int fd_child_to_parent[2];
int fd_parent_to_child[2];

r = pipe(fd_child_to_parent)
if (r != -1) {
exit(EXIT_FAILURE);
}
r = pipe(fd_parent_to_child)
if (r != -1) {
exit(EXIT_FAILURE);
}


Je ne suis pas certain de comprendre. Si j'interprète le code au 1er
degré, le déroulement normal du programme c'est quand pipe renvoie une
erreur. Je pense donc qu'il y a maldonne. Est-ce que c'est pas plutôt
(r == -1) ou encore (r != 0) ? Mon manuel dit « On successful creation
of the pipe, zero is returned. Otherwise, a value of -1 is
returned... » (désolé pour la francophonie, mon manuel n'est pas
touboniste).

r = fork();
if (r == -1) {
exit(EXIT_FAILURE);
}


Là, je suis !

if (r == 0) {
/* processus fils */

r = dup2(fd_child_to_parent[1], 1);


On essaie de réaffecter l'entrée du pipe à « l'objet nº 1 » si je suis bien.
Est-ce que ça veut dire que cet objet nº 1 est la sortie standard ?

Je trouve le manuel de dup2 plutôt sybillin.

if (r < 0) {
exit(EXIT_FAILURE);
}
close(fd_child_to_parent[1]);
close(fd_child_to_parent[0]);


On ferme ce pipe ? Ça veut dire que le fils n'en a plus besoin, mais
le père ne sera pas affecté par ces fermetures ; et quand le fils
écrira sur sa sortie standard, ça arrivera dans fd_child_to_parent[0]
chez le père.

J'ai bon ?

r = dup2(fd_parent_to_child[0], 0);
if (r < 0) {
exit(EXIT_FAILURE);
}
close(fd_parent_to_child[0]);
close(fd_parent_to_child[1]);


Si je comprends toujours bien, l'objet nº 0 est l'entrée standard, et
le fils va voir arriver sur son entrée standard tout ce que le père
envoie sur fd_parent_to_child[1] ?

execl("programme", "programme", "arg1", "arg2", NULL);


Et là on lance le programme. Il a l'entrée et la sortie standard du
processus qui l'a lancé, manquerait plus qu'il fasse le mariole. Tu me
dis si je me gourre.

}
else {


Maintenant on est chez papa.

char buf[256];

close(fd_parent_to_child[0]);


Papa n'a pas besoin de la sortie du tuyau qui lui sert à parler à
bébé. Il la ferme mais ceci n'intervient pas sur le tuyau lui-même
(puisque du côté du processus fils ça reste ouvert, le pipe continue à
exister dans le système) mais seulement sur sa possibilité de
l'utiliser -- ce qu'il n'a justement pas à faire.

C'est ça ? C'est être propre qu'on ferme (ce que j'apprécie) ou il y a
une autre utilité ?

/* processus père */
r = write(fd_parent_to_child[1], "toto", 4);


Le père dit "toto" à son fils.

if (r < 0) {
exit(EXIT_FAILURE);


À priori cette erreur ne survient que si le fils n'a pas réussi à
déplacer le bout du pipe avec dup2, oui ?

}

close(fd_child_to_parent[0]);


Je suppose que c'est fd_child_to_parent[1] qu'il faut fermer, non ?
Et ça aurait pu être fait avant d'écrire le moindre "toto"... non ?

r = read(fd_child_to_parent[0], buf, sizeof(buf));
if (r < 0) {
exit(EXIT_FAILURE);
}
...
}
}


Blou.

Comme je te le disais je n'ai pas encore fait d'essais ; j'en ferai ce
soir, et si je ne m'en sors pas je reviendrai abuser de ta bonté.

Merci beaucoup.


Avatar
DINH Viêt Hoà

Petit exemple :

{
int r;
int fd_child_to_parent[2];
int fd_parent_to_child[2];

r = pipe(fd_child_to_parent)
if (r != -1) {
exit(EXIT_FAILURE);
}
r = pipe(fd_parent_to_child)
if (r != -1) {
exit(EXIT_FAILURE);
}


Je ne suis pas certain de comprendre. Si j'interprète le code au 1er
degré, le déroulement normal du programme c'est quand pipe renvoie une
erreur. Je pense donc qu'il y a maldonne. Est-ce que c'est pas plutôt
(r == -1) ou encore (r != 0) ? Mon manuel dit « On successful creation
of the pipe, zero is returned. Otherwise, a value of -1 is
returned... » (désolé pour la francophonie, mon manuel n'est pas
touboniste).


oui, c'est bien un (r < 0) qu'il fallait lire.

r = fork();
if (r == -1) {
exit(EXIT_FAILURE);
}


Là, je suis !

if (r == 0) {
/* processus fils */

r = dup2(fd_child_to_parent[1], 1);


On essaie de réaffecter l'entrée du pipe à « l'objet nº 1 » si je suis bien.
Est-ce que ça veut dire que cet objet nº 1 est la sortie standard ?

Je trouve le manuel de dup2 plutôt sybillin.


et fd_child_to_parent[1] reste ouvert, donc, on le faire plutôt que de
garder deux fois les mêmes chose.

if (r < 0) {
exit(EXIT_FAILURE);
}
close(fd_child_to_parent[1]);
close(fd_child_to_parent[0]);


On ferme ce pipe ? Ça veut dire que le fils n'en a plus besoin, mais
le père ne sera pas affecté par ces fermetures ; et quand le fils
écrira sur sa sortie standard, ça arrivera dans fd_child_to_parent[0]
chez le père.

J'ai bon ?


oui.

r = dup2(fd_parent_to_child[0], 0);
if (r < 0) {
exit(EXIT_FAILURE);
}
close(fd_parent_to_child[0]);
close(fd_parent_to_child[1]);


Si je comprends toujours bien, l'objet nº 0 est l'entrée standard, et
le fils va voir arriver sur son entrée standard tout ce que le père
envoie sur fd_parent_to_child[1] ?


oui.

execl("programme", "programme", "arg1", "arg2", NULL);


Et là on lance le programme. Il a l'entrée et la sortie standard du
processus qui l'a lancé, manquerait plus qu'il fasse le mariole. Tu me
dis si je me gourre.


c'est bon.

}
else {


Maintenant on est chez papa.

char buf[256];

close(fd_parent_to_child[0]);


Papa n'a pas besoin de la sortie du tuyau qui lui sert à parler à
bébé. Il la ferme mais ceci n'intervient pas sur le tuyau lui-même
(puisque du côté du processus fils ça reste ouvert, le pipe continue à
exister dans le système) mais seulement sur sa possibilité de
l'utiliser -- ce qu'il n'a justement pas à faire.

C'est ça ? C'est être propre qu'on ferme (ce que j'apprécie) ou il y a
une autre utilité ?


non, juste que c'est plus propre de le fermer.

/* processus père */
r = write(fd_parent_to_child[1], "toto", 4);


Le père dit "toto" à son fils.

if (r < 0) {
exit(EXIT_FAILURE);


À priori cette erreur ne survient que si le fils n'a pas réussi à
déplacer le bout du pipe avec dup2, oui ?


toujours vérifier les erreurs possible !

si dup2() échoue dans le fils, comme tu le vois, il y a un exit() qui
provoque la fermeture de tous les descripteurs de fichier.
Et si on écrit sur un pipe() et que l'autre bout n'est pas ouvert, cela
retourne -1 si on a redirigé le signal SIGPIPE à SIG_IGN, et cela provoque
l'arrêt du programme par le signal SIGPIPE si jamais on n'a pas touché aux
gestionnaire de signaux (me corriger sur ce dernier point si j'ai faux).

}

close(fd_child_to_parent[0]);


Je suppose que c'est fd_child_to_parent[1] qu'il faut fermer, non ?
Et ça aurait pu être fait avant d'écrire le moindre "toto"... non ?


tout à fait.

r = read(fd_child_to_parent[0], buf, sizeof(buf));
if (r < 0) {
exit(EXIT_FAILURE);
}
...
}
}



--
DINH V. Hoa,

"monde de merde" -- Erwan David


Avatar
un.gabacho.sans.pourrier
DINH Viêt Hoà a encore répondu.



Merci beaucoup !