#1 Le 30/11/2015, à 14:06
- DonutMan75
[RESOLU] [C] Exemple d'utilisation de la fonction dup()
Bonjour à tous,
j'essaie de faire fonctionner la fonction dup sur un exemple simple : un programme fork
- Le fils lance un ls et redirige sa sortie standard vers un tube anonyme
- Le père attend que son fils termine, redirige son entrée standard sur l'entrée du tube anonyme puis lance un wc -l
Comportement attendu : le nombre de fichiers du répertoire courant devrait s'afficher sur le terminal
Comportement obtenu : le programme "attend" (rien ne s'affiche et il faut faire Ctrl-C pour quitter).
Après quelques tests, il me semble que wc -l du père ne lit rien sur son entrée.... Avez-vous des idées ?
Merci d'avance
#include <stdio.h>
#include <unistd.h>
main()
{
int p[2];
int pid, pid_ret, status;
pipe(p);
printf("Pipe ouvert\n\t- Lecture :\t%d\n\t- Ecriture :\t%d\n", p[0], p[1]);
switch( (pid = fork()) )
{
case 0: // FILS
close(STDOUT_FILENO);
dup(p[1]); //Le premier descripteur libre (stdout) pointe maintenant vers p[1] (écriture dans tube anonyme)
close(p[1]); // Desormais inutile
execlp("ls", "ls", "-l", NULL);
break;
default: // PERE
printf("Le PERE attend la fin du fils...\n");
pid_ret = waitpid(pid, &status, 0);
printf(" ok...\n");
close(STDIN_FILENO);
dup(p[0]); // Le premier descripteur libre (stdin) pointe maintenant vers p[0] (lecture depuis tube anonyme)
close(p[0]);
printf("Execution...\n");
execlp("wc", "wc", "-l", NULL);
}
}
Dernière modification par DonutMan75 (Le 30/11/2015, à 17:59)
Hors ligne
#2 Le 30/11/2015, à 15:10
- claudius01
Re : [RESOLU] [C] Exemple d'utilisation de la fonction dup()
Bonjour,
Tout d'abord, je ne comprends pas ton utilisation de dup() qui retourne normalement une copie du descripteur passé en argument.
Essaye dans un premier temps de tester le retour de dup() à des fins de tests ;-)...
cf. un exemple de dup (C System Call) (2nd exemple "You can also use dup() to redirect standard output by taking advantage of the fact that it always uses the smallest available file descriptor" ;-)
Edit: Si j'ai bien compris, tu souhaites faire:
$ ls -l | wc -l
avec le ls lancé par le fils et wc par le père...
A suivre...
Dernière modification par claudius01 (Le 30/11/2015, à 17:28)
Hors ligne
#3 Le 30/11/2015, à 17:39
- DonutMan75
Re : [RESOLU] [C] Exemple d'utilisation de la fonction dup()
Bonjour Claudius,
merci pour ta réponse rapide.
En effet je cherche bien à reproduire
ls -l | wc -l
Mais ici :
- le fils lance ls
- le père attends la fin de son fils puis lance wc -l
J'ai lu avec beaucoup d'intérêt le lien que tu m'as donné et j'ai fais tourner (avec succès ) le code du deuxième exemple.
En fait, si j'ai bien compris, l'utilisation primaire de "int tata = dub(toto)" est de dupliquer le descripteur toto dans tata. La valeur de tata est le premier entier libre dans la table des descripteurs du processus.
Désormais un puts() n'écrira plus sur la sortie standard mais dans le fichier "output".
Dans le cas de mon code (en fait il est tiré du livre "Linux : Programmation système et réseau" de Joëlle Delacroix aux éditions Dunod, chapitre 7 page 218), il me semble qu'on fait exactement la même chose sauf qu'on lit/écrit dans un tube anonyme au lieu d'un fichier normal...
En clair les différentes étapes sont :
Initialisation (étape commune au père et au fils) : je crée un tube anonyme p
- p[0] = sortie du tube (lecture)
- p[1] = entrée du tube (écriture)
Les étapes du le fils (celui qui ECRIT dans le tube anonyme) :
1 - je ferme stdout (par convention stdout = 1)
2 - je duplique le descripteur p[1] : comme c'est la plus petite valeur libre qui lui sera affectée et que stdout est disponible alors stdout va pointer vers l'entrée du tube p
3 - j'écris quelque chose dans le tube (ici ls -l)
Les étapes du père (celui qui LIT dans le tube anonyme) :
1 - j'attends que mon fils se termine
2 - je ferme stdin (par convention stdin = 0)
3 - je duplique le descripteur p[0] : de la même façon, stdin va maintenant pointer vers la sortie du tube p
4 - j'écrase mon code avec exec("wc -l")
Mais j'ai l'impression que wc -l "attend" quelque chose avant d'afficher le résultat... C'est un peu comme si j'écrivais dans le shell
tail -f toto.txt | wc -l
Et je ne vois pas trop comment m'en sortir...
Hors ligne
#4 Le 30/11/2015, à 17:48
- DonutMan75
Re : [RESOLU] [C] Exemple d'utilisation de la fonction dup()
Aaah j'ai trouvé !!!
En fait, tant que le nombre d'écrivains du tube n'est pas nul, il est toujours possible que quelqu'un vienne écrire dans le pipe.
Et donc wc lit la sortie du tube jusqu'à qu'il reçoive une fin de fichier. Cette fin de fichier n'est envoyée que lorsque le nombre d'écrivain est égal à 0.
Du côté du fils, tout est en règle car il s'est terminé correctement. (même si un close(p[0]) en plus du close(p[1]) aurait été mieux)
Du côté du père en revanche, l'absence de close(p[1]) est déterminante ! Comme le père peut toujours écrire dans le tube, wc -l poireaute...
J'ai rajouté les close qu'il faut et tout marche nickel
Je remarque au passage que la commande ls -l | wc -l renvoie le même résultat que le schell MOINS DEUX (par exemple si ls -l | wc -l renvoie 50 dans mon schell, avec mon code C ça me renvoie 48...). Après examen, il se trouve que le execlp("ls", "ls", "-l", NULL); ne retourne ni "." ni ".."', curieux non ?
Merci et bonne soirée
Donut.
Dernière modification par DonutMan75 (Le 30/11/2015, à 17:50)
Hors ligne
#5 Le 30/11/2015, à 17:59
- pingouinux
Re : [RESOLU] [C] Exemple d'utilisation de la fonction dup()
Bonjour,
Il est normal que ls -l ne retourne ni . ni ..
Pour les avoir, c'est ls -la
Hors ligne
#6 Le 30/11/2015, à 18:06
- DonutMan75
Re : [RESOLU] [C] Exemple d'utilisation de la fonction dup()
Bonjour pingouinux,
ah bah tient oui, j'avais un alias dans mon bash_profile, tout s'explique !
alias ls='\ls -a'
Hors ligne
#7 Le 30/11/2015, à 18:10
- claudius01
Re : [RESOLU] [C] Exemple d'utilisation de la fonction dup()
... Aaah j'ai trouvé !!!
Bravo, entre temps et en mettant en œuvre ton programme j'ai trouvé si cela t’intéresse: Using dup2 for I/O Redirection and Pipes dont j'ai un peu modifié l'exemple 'pipe.c' comme suit pour rejoindre ton cas d'application; à savoir:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char **argv)
{
int pipefd[2];
int pid;
char *ll_args[] = {"ls", "-l", NULL};
char *wc_args[] = {"wc", "-l", NULL};
// make a pipe (fds go in pipefd[0] and pipefd[1])
pipe(pipefd);
pid = fork();
if (pid == 0)
{
// child gets here and handles "wc -l"
// replace standard input with input part of pipe
dup2(pipefd[0], 0);
// close unused hald of pipe
close(pipefd[1]);
// execute wc -l
execvp("wc", wc_args);
}
else
{
// parent gets here and handles "ls -l"
// replace standard output with output part of pipe
dup2(pipefd[1], 1);
// close unused unput half of pipe
close(pipefd[0]);
// execute ls -l
execvp("ls", ll_args);
}
return 0;
}
et qui donne bien
$ gcc -Wall -o pipe pipe.c
$ ./pipe
$ 13
^C
$ ls -l | wc -l
13
NB: Une seule chose me chagrine, c'est l'obligation de faire un Ctrl^C ou un Carriage Return pour retrouver le prompt ;-(
Dernière modification par claudius01 (Le 30/11/2015, à 19:26)
Hors ligne