Nous travaillons sur le programme 
prog06_redir_exec.cpp.
Ce programme nous montrera que les tubes anonymes facilitent la réutilisation
  de programmes déjà existants par redirection d'entrées/sorties et
  recouvrement dans un processus enfant.
En l'état, le programme fourni est assez proche de ce qui a été réalisé
  
à l'étape précédente : une communication par tube est mise
  en place entre un processus enfant et son processus parent.
Seulement, quand on exécute ce programme
$ ./prog06_redir_exec ↵
  nous constatons que le processus parent ne détecte rien d'autre que
  la fin de fichier en provenance du processus enfant.
En effet, ce dernier, lorsque aucun paramètre n'est transmis sur la
  ligne de commande du programme, se contente d'afficher un message
  sur sa sortie standard avec 
crs::out() puis se termine avec
  
crs::exit().
Le tube n'est donc pas du tout utilisé par le processus enfant, ce qui
  explique que le processus parent n'en obtient rien.
Intervenez alors au niveau du point 
{1} du programme fourni afin de
  procéder à la 
redirection de la sortie standard du processus enfant
  vers le tube (selon les recommandations des commentaires).
L'appel système 
dup2() sert à indiquer que la ressource désignée
  par son premier descripteur de fichier en paramètre (le côté écriture
  du tube ici) doit également être désignée par son second descripteur de
  fichier en paramètre (la sortie standard ici).
Juste avant cet appel, le descripteur de fichier de la sortie standard
  désignait le terminal ; juste après cet appel il désigne dorénavant
  le côté écriture du tube (tout comme le premier paramètre)
De la même façon que plusieurs pointeurs peuvent désigner une même donnée
      en mémoire, plusieurs descripteurs de fichier peuvent désigner la
      même ressource de communication ; c'est le cas suite à l'appel
      système dup2().
.
Désormais, écrire dans le descripteur de fichier de la sortie standard
  provoquera le même effet qu'écrire dans le descripteur de fichier
  qui désigne le côté écriture du tube.
Ce dernier descripteur de fichier devient donc inutile puisque la sortie
  standard désigne dorénavant la ressource que nous avons l'intention
  d'utiliser (le côté écriture du tube) ; il est alors recommandé de
  fermer ce descripteur de fichier.
En relançant l'exécution de ce même programme, vous devriez constater
  que le message produit par le processus enfant dans sa sortie standard
  (
crs::out()) n'apparaît plus directement dans le terminal mais
  est bien extrait depuis le tube par le processus parent qui peut
  l'exploiter à sa convenance
Dans cet exemple simpliste le processus parent se contente d'afficher
      le message extrait du tube (en provenance du processus enfant) mais
      de manière plus générale la réception d'un tel message pourrait servir
      à alimenter n'importe un traitement utile à l'application.
.
En l'état, nous pourrions objecter que la redirection de la sortie standard
  vers le tube n'apporte pas grand chose par rapport à une écriture directe
  dans le tube comme nous  le faisions 
à l'étape précédente.
En effet, en terme de rédaction de code source, il n'est pas plus compliqué
  d'écrire dans un tube que dans la sortie standard.
Cependant, cette démarche prend tout son intérêt si après cette redirection
  nous invoquons du code déjà existant qui écrit systématiquement dans
  la sortie standard (parce qu'il ignore simplement l'existence de ce
  tube).
Ce cas de figure est très fréquent lorsque nous effectuons un
  
recouvrement de processus.
Pour mettre en œuvre cette démarche de recouvrement, nous allons utiliser
  comme prétexte la décompression d'un fichier au format 
"gzip".
Tout d'abord, en vue de l'expérimentation, obtenons un simple fichier qui
  contient du texte compressé grâce à la commande 
shell suivante :
$ ls -l | gzip > output_file.gz ↵
  (la liste détaillée du contenu du répertoire courant, affichée par la
  commande 
ls -l, est compressée par la commande 
gzip
Sans argument, la command 
gzip se contente de lire tout ce qui
      arrive sur son entrée standard, le compresse, et inscrit le
      contenu compressé sur sa sortie standard.
    Ici le symbole 
|
 (
pipe) du 
shell relie la sortie
      standard de la commande 
ls -l (des lignes de texte) à l'entrée
      standard de la commande 
gzip grâce à des redirections dans un
      tube (exactement comme ce que nous sommes en train d'étudier !).
    Le symbole 
>
 du 
shell redirige la sortie standard de la
      commande 
gzip vers le fichier indiqué juste après.
 et stockée
  dans 
output_file.gz).
Nous souhaitons donc que notre processus enfant consomme le contenu compressé
  du fichier 
output_file.gz, le décompresse et produise le contenu
  décompressé dans le tube de communication afin que le processus parent
  l'obtienne.
Seulement, pour réaliser cette opération nous allons nous appuyer
  sur l'utilisation telle quelle de la commande 
gunzip dans notre
  processus enfant.
En effet, cette commande, lorsqu'elle est utilisé sans argument, se contente
  de lire tout ce qui arrive sur son entrée standard, le décompresse, et
  inscrit le contenu décompressé sur sa sortie standard.
Il s'agit alors dans un premier temps d'effectuer les redirections
  d'entrée/sortie attendues.
Nous avions déjà redirigé la sortie standard au niveau du point
{1} ; il faut maintenant compléter le point 
{2} d'une
  manière très similaire afin que l'entrée standard du processus enfant ne
  corresponde plus à la saisie au clavier mais au fichier préalablement
  ouvert en lecture.
Il reste enfin à faire en sorte que le processus enfant exécute le code
  de la commande 
gunzip.
C'est ici qu'intervient la notion de recouvrement : il s'agit de
  complètement se débarrasser du code et des données d'un processus
  pour les remplacer par un tout nouveau programme qui démarre son
  exécution.
La fonction utilitaire 
crs::exec() (qui repose en interne sur l'appel
  système 
execve()) attend comme paramètre un tableau de chaînes
  qui sera interprété comme une ligne de commande : la première chaîne
  est le nom du programme, la seconde son premier argument...
Complétez alors le point 
{3} pour recouvrir le processus enfant par
  la simple commande 
gunzip.
Lorsque cet appel réussi (le programme exécutable existe) nous n'en revenons
  jamais !
En effet, tout le code qu'exécutait le processus enfant vient d'être remplacé
  par celui du programme choisi ; toute instruction qui suivait l'appel
  à 
crs::exec() n'est désormais plus accessible à ce processus.
Toutefois, ce recouvrement ne concerne que le processus enfant ; le
  processus parent n'a pas invoqué 
crs::exec() et continue d'exécuter
  son code pour exploiter ce qu'il extrait du tube de communication.
Il est important de noter que, bien que tout le code et toutes les données
  d'un processus sont remplacés lors d'un recouvrement, ses moyens de
  communication (tubes, fichiers...) restent ouverts et les redirections
  restent effectives.
Ainsi, le processus enfant qui exécute désormais la commande 
gunzip
  a bien son entrée standard reliée au fichier choisi et sa sortie
  standard au coté écriture du tube.
Dans ces conditions, vous pouvez tester votre programme en précisant
  le nom du fichier compressé sur sa ligne de commande.
$ ./prog06_redir_exec output_file.gz ↵
Vous devriez constater que le processus parent obtient bien (et affiche) le
  contenu décompressé du fichier en question
Si vous souhaitez visualiser le contenu décompressé de ce fichier
      depuis le shell (sans votre programme) vous pouvez utiliser
      la commande suivante :
      $ gunzip < output_file.gz ↵
.
Grâce à la création d'un processus enfant, la redirection des ses
  entrée/sortie standards et le recouvrement par un autre programme,
  nous sommes parvenus à utiliser dans notre propre programme (le
  processus parent) les services d'un programme existant (la commande
  
gunzip) en l'utilisant tel quel, sans avoir à le réécrire pour
  l'adapter à notre besoin.
Le recouvrement que nous venons d'expérimenter ici est un élément central des
  systèmes d'exploitation.
Il faut savoir que tous les programmes que nous lançons dans un système
  d'exploitation (automatiquement au démarrage, par la saisie d'une commande,
  en cliquant dans un outil...) reposent sur ce mécanisme.
Puisqu'il remplace le contenu d'un processus existant, il est très courant
  de faire apparaître un processus enfant juste à cette occasion, ce qui
  permet de conserver l'activité initiale du processus parent ; la
  séquence d'appels système 
fork()/
execve() est très représentative
  du lancement d'un nouveau programme.
Les opérations de redirections d'entrées/sorties avec l'appel système
  
dup2() prennent tout leur intérêt dans ce contexte car cela permet de
  relier entre eux et faire travailler de concert des programmes qui n'avaient
  pas été explicitement conçus dans ce but.
C'est d'ailleurs un point marquant de la manière dont ont été conçus les
  systèmes d'exploitation de la famille UNIX : proposer une multitude
  de commandes élémentaires et des moyens d'interconnexion pour les faire
  collaborer afin de réaliser des services de plus haut niveau
Les adeptes de la ligne de commande du 
shell doivent reconnaître
      ceci à travers l'usage des symboles
|
, 
>
, 
<
...
.
Documentation utile :
- man 1 gunzip
 
- crs::pipe()    → man 2 pipe
 
- crs::fork()    → man 2 fork
 
- crs::waitpid() → man 2 waitpid
 
- crs::close()   → man 2 close
 
- crs::openR()   → man 2 open
 
- crs::dup2()    → man 2 dup2
 
- crs::exec()    → man 2 execve
 
- crs::read()    → man 2 read