Tubes de communication (pipes)

Exercice 1

Ecrire un programme C comportant un processus producteur (lecture de l'entrée standard et écriture dans un tube) et un processus consommateur qui met en majuscule les minuscules (lecture du tube et écriture sur la sortie standard).

Corrigé

#include <ctype.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>

#define BUFSZ 1024

void minmaj(char *buf, int sz)
{  int i;
   for (i = 0; i < sz; i++)
      buf[i] = toupper(buf[i]);
}


/* Note : Les deux fonctions suivantes ne sont pas "propres".
   Il faut toujours tester la valeur de retour de read et write
*/

void copy(int in, int out, void *buf, int sz)
{  while (write(out, buf, read(in, buf, sz)));
}


void copymm(int in, int out, void *buf, int sz)
{  int n;

   while ((n = read(in, buf, sz)))
   {  minmaj(buf, n);
      write(out, buf, n);
   }
}


int main(void)
{  int p[2];
   pid_t pid;
   static char buf[BUFSZ];

   if (pipe(p))
   {  perror("pipe"); exit(1);
   }
   if ((pid = fork()) == -1)
   {  perror("fork"); exit(1);
   }
   if (pid)
   {  close(p[0]);
      copy(STDIN_FILENO, p[1], buf, BUFSZ);
      close(p[1]);
      wait(NULL);
   }
   else
   {  close(p[1]);
      copymm(p[0], STDOUT_FILENO, buf, BUFSZ);
      close(p[0]);
   }
   return 0;
}

Exercice 2

Ecrire un programme C qui crée deux processus A et B communiquant par tube de communication. Le processus A ouvre un fichier donné en argument du programme et transmet le contenu de ce fichier au processus B via un tube de communication. Le processus B écrit le contenu du tube dans le deuxième fichier passé en argument.

Corrigé

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>

#define BUFSZ 1024


/* Note : La fonction suivante n'est pas "propre".
   Il faut toujours tester la valeur de retour de read et write
*/

void copy(int in, int out, void *buf, int sz)
{  while (write(out, buf, read(in, buf, sz)));
}



int main(int argc, char **argv)
{  int p[2], fd;
   pid_t pid;
   static char buf[BUFSZ];

   if (argc != 3)
   {  fprintf(stderr, "Usage : %s fichier1 fichier2\n", argv[0]);
      exit(1);
   }
   if (pipe(p))
   {  perror("pipe"); exit(1);
   }
   if ((pid = fork()) == -1)
   {  perror("fork"); exit(1);
   }
   if (pid)
   {  close(p[0]);
      if ((fd = open(argv[1], O_RDONLY)) == -1)
      {  perror("open"); kill(pid, SIGKILL); exit(1);
      }
      copy(fd, p[1], buf, BUFSZ);
      close(p[1]);
      close(fd);
      wait(NULL);
   }
   else
   {  close(p[1]);
      if ((fd = creat(argv[2], 0664)) == -1)
      {  perror("open"); kill(getppid(), SIGKILL); exit(1);
      }
      copy(p[0], fd, buf, BUFSZ);
      close(p[0]);
      close(fd);
   }
   return 0;
}

Exercice 3

Ecrire un programme C équivalente à la commande shell
ps -uax | grep root | wc -l

Corrigé

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>


int main(void)
{  int p[2];
   pid_t pid;

   if (pipe(p) == -1)
   {  perror("pipe"); exit(1);
   }
   if ((pid = fork()) == -1)
   {  perror("fork");
      exit(1);
   }
   if (pid)
   {  close(p[0]);
      dup2(p[1], STDOUT_FILENO);
      execlp("ps", "ps", "-uax", NULL);
      perror("execlp"); exit(1);
   }
   close(p[1]);
   dup2(p[0], STDIN_FILENO);
   if (pipe(p) == -1)
   {  perror("pipe"); exit(1);
   }
   if ((pid = fork()) == -1)
   {  perror("fork");
      exit(1);
   }
   if (pid)
   {  close(p[0]);
      dup2(p[1], STDOUT_FILENO);
      execlp("grep", "grep", "root", NULL);
      perror("execlp"); exit(1);
   }
   close(p[1]);
   dup2(p[0], STDIN_FILENO);
   execlp("wc", "wc", "-l", NULL);
   perror("execlp");
   exit(1);
}

Exercice 4

Une manière de déterminer les nombres premiers dans un intervalle est de découper l'espace de recherche en plusieurs sous-intervalles et d'affecter la recherche dans chaque intervalle à un processus créé par le père. Ainsi si on découpe l'intervalle [1, N] en p sous-intervalles, le processus père lance p fils et le fils k recherche l'intervalle [kN/p + 1, (k + 1)N/p], k = 0,...,p - 1. Cette technique permet, si on dispose d'une machine équipée de p processeurs, de paralléliser la recherche (et d'essayer de retourner le résultat p fois plus vite). Elle a cependant l'inconvénient d'affecter des espaces de recherche dont les temps d'exploration sont inégaux. Ainsi le processus 0 terminera la recherche sur [1, N/p] bien avant le processus p - 1. Il (et donc un des processeurs de la machine) sera donc oisif jusqu'à la fin du programme. La charge de travail entre les processus (et donc les processeurs) est inégalement repartie.

Une solution (celle que vous devez programmer) pour repartir la charge de travail, consiste a utiliser un « pool » de p processus travailleurs à qui un processus maître affecte successivement des travaux de recherche de nombres premiers dans de petits intervalles (taille T << N/p). Quand un travailleur a fini sa recherche, le maître lui affecte la recherche dans un intervalle encore inexploré.

Pour votre implantation :

 

Envoyez votre programme ici. N'oubliez pas à préciser votre nom et prénom.