Pour éviter de passer le descripteur, il est possible d'exploiter les descripteurs par défaut déjà existants : STDIN (0), STDOUT (1) et STDERR (2). Les processus doivent bien sûr au préalable se mettre d'accord sur la convention d'utilisation de chacun de ces descripteurs.
Pour utiliser un descripteur donné, il faut qu'il soit disponible. On peut alors associer le descripteur du pipe que l'on veut créer au descripteur standard grâce à la fonction dup() qui duplique le descripteur passé en paramètre et renvoie la plus petite valeur de descripteur non utilisé :
int dup(int oldfd);
Pour obtenir un descripteur donné, il faut donc le fermer et utiliser la fonction dup() jusqu'à ce que le descripteur voulu soit obtenu... cela peut être fastidieux !
La solution consiste à utiliser la fonction dup2() qui demande explicitement la duplication d'un descripteur donné :
int dup2(int oldfd, int newfd);
Dans l'exemple ci-dessous, on fait communiquer 2 processus par un pipe en utilisant le descripteur 0.
[linewidth=1pt,fillstyle=solid,shadow=true](-0.5,0)(17.5,-24)
Le père lit dans le pipe dans lequel le fils envoie des informations.
/* Programme du pere */ #include <stdio.h> <sys/types.h> <sys/stat.h> void main() { int pipdes[2],status,len; char msg[80]; struct stat pipestat; if (pipe(pipdes)) exit(1); if (fork()) { close(pipdes[1]); if (dup2(pipdes[0],0)==-1) exit(2); do { if (fstat(0,&pipestat)) exit(3); len=(int)pipestat.st_size; } while(len==0); if (read(0,msg,len)!=len) exit(4); msg[len]=0; printf("%s\n",msg); wait(&status); exit(0); } else { close(pipdes[0]); if (dup2(pipdes[1],1)==-1) exit(2); execl("pipe_fils2","pipe_fils2",0); } } /* Programme du fils : pipe_fils2 */ #include <stdio.h> void main(int argc, char ** argv) { char *msg="Salut papa, je suis ton fils"; if (write(1,msg,strlen(msg))!=strlen(msg)) exit(1); exit(0); }
Exercice : Utiliser la commande dup() pour connecter 2 commandes UNIX (ls et sort) par un pipe. La première commande écrit dans le pipe en utilisant la sortie standard tandis que la deuxième lit le pipe par l'entrée standard. Le processus père crée le pipe puis le processus qui mettra en place la première commande. Par la suite il crée le deuxième processus.
Le processus fils 1 ferme le descripteur 1 pour le rendre disponible, y associe l'entrée du pipe (pour y écrire via STDOUT) puis ferme les descripteurs initiaux du pipe. Il met en place la première commande. Le second processus, ferme le descripteur 0, associe le pipe (pour lire via STDIN), ferme les descripteurs initiaux et met en place la deuxième commande.
Il est indispensable de fermer les descripteurs initiaux du pipe pour que celui-ci disparaisse normalement à la mort des processus sinon il y a blocage du deuxième processus (et donc du père) en attente sur la fermeture du pipe.
Le programme réalise l'équivalent de la ligne de commande ls | sort. C'est ce principe qui est utilisé par le shell pour effectuer les redirections et la connexion des commandes par pipe. Vous pouvez utiliser ce principe pour créer votre propre shell.