Communication inter-processus - Tuyaux

Le tuyau est un moyen de communication entre deux ou plusieurs processus liés ou interdépendants. Il peut s'agir d'un processus ou d'une communication entre l'enfant et les processus parents. La communication peut également être à plusieurs niveaux, comme la communication entre le parent, l'enfant et le petit-enfant, etc. La communication est réalisée par un processus qui écrit dans le tube et une autre lecture depuis le tube. Pour réaliser l'appel système de canal, créez deux fichiers, un pour écrire dans le fichier et un autre pour lire à partir du fichier.

Le mécanisme du tuyau peut être visualisé avec un scénario en temps réel tel que le remplissage d'eau avec le tuyau dans un récipient, par exemple un seau, et quelqu'un le récupérant, par exemple avec une tasse. Le processus de remplissage n'est rien d'autre que l'écriture dans le tuyau et le processus de lecture n'est rien d'autre que la récupération du tuyau. Cela implique qu'une sortie (eau) est entrée pour l'autre (seau).

#include<unistd.h>

int pipe(int pipedes[2]);

Cet appel système créerait un canal pour une communication unidirectionnelle, c'est-à-dire qu'il crée deux descripteurs, le premier est connecté pour lire à partir du tube et l'autre est connecté pour écrire dans le tube.

Le descripteur pipedes [0] est pour la lecture et pipedes [1] pour l'écriture. Tout ce qui est écrit dans pipedes [1] peut être lu à partir de pipedes [0].

Cet appel renverrait zéro en cas de succès et -1 en cas d'échec. Pour connaître la cause de l'échec, vérifiez avec la variable errno ou la fonction perror ().

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

Même si les opérations de base pour le fichier sont en lecture et en écriture, il est essentiel d'ouvrir le fichier avant d'effectuer les opérations et de fermer le fichier une fois les opérations requises terminées. Habituellement, par défaut, 3 descripteurs ouverts pour chaque processus, qui sont utilisés pour l'entrée (entrée standard - stdin), la sortie (sortie standard - stdout) et l'erreur (erreur standard - stderr) ayant respectivement des descripteurs de fichier 0, 1 et 2.

Cet appel système renverrait un descripteur de fichier utilisé pour d'autres opérations de fichier de lecture / écriture / recherche (lseek). Habituellement, les descripteurs de fichiers commencent à partir de 3 et augmentent d'un nombre en fonction du nombre de fichiers ouverts.

Les arguments passés pour ouvrir l'appel système sont le chemin (chemin relatif ou absolu), les drapeaux mentionnant le but de l'ouverture du fichier (par exemple, ouverture pour lecture, O_RDONLY, pour écrire, O_WRONLY, pour lire et écrire, O_RDWR, pour ajouter au fichier existant O_APPEND, pour créer un fichier, s'il n'existe pas avec O_CREAT et ainsi de suite) et le mode requis fournissant des autorisations de lecture / écriture / exécution pour l'utilisateur ou le propriétaire / groupe / autres. Le mode peut être mentionné avec des symboles.

Lecture - 4, écriture - 2 et exécution - 1.

Par exemple: valeur octale (commence par 0), 0764 implique que le propriétaire a des autorisations de lecture, d'écriture et d'exécution, le groupe a des autorisations de lecture et d'écriture, l'autre a des autorisations de lecture. Cela peut également être représenté par S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH, ce qui implique ou opération de 0700 | 0040 | 0020 | 0004 → 0764.

Cet appel système, en cas de succès, retourne le nouveau descripteur de fichier id et -1 en cas d'erreur. La cause de l'erreur peut être identifiée avec la variable errno ou la fonction perror ().

#include<unistd.h>

int close(int fd)

L'appel système ci-dessus fermant le descripteur de fichier déjà ouvert. Cela implique que le fichier n'est plus utilisé et que les ressources associées peuvent être réutilisées par tout autre processus. Cet appel système renvoie zéro en cas de succès et -1 en cas d'erreur. La cause de l'erreur peut être identifiée avec la variable errno ou la fonction perror ().

#include<unistd.h>

ssize_t read(int fd, void *buf, size_t count)

L'appel système ci-dessus consiste à lire à partir du fichier spécifié avec les arguments du descripteur de fichier fd, le tampon approprié avec la mémoire allouée (statique ou dynamique) et la taille du tampon.

L'identifiant du descripteur de fichier sert à identifier le fichier respectif, qui est renvoyé après l'appel système open () ou pipe (). Le fichier doit être ouvert avant de lire à partir du fichier. Il s'ouvre automatiquement en cas d'appel système pipe ().

Cet appel renverrait le nombre d'octets lus (ou zéro en cas de rencontre avec la fin du fichier) en cas de succès et -1 en cas d'échec. Les octets de retour peuvent être plus petits que le nombre d'octets demandés, juste au cas où aucune donnée n'est disponible ou si le fichier est fermé. Un numéro d'erreur correct est défini en cas d'échec.

Pour connaître la cause de l'échec, vérifiez avec la variable errno ou la fonction perror ().

#include<unistd.h>

ssize_t write(int fd, void *buf, size_t count)

L'appel système ci-dessus consiste à écrire dans le fichier spécifié avec des arguments du descripteur de fichier fd, un tampon approprié avec de la mémoire allouée (statique ou dynamique) et la taille du tampon.

L'identifiant du descripteur de fichier sert à identifier le fichier respectif, qui est renvoyé après l'appel système open () ou pipe ().

Le fichier doit être ouvert avant d'écrire dans le fichier. Il s'ouvre automatiquement en cas d'appel système pipe ().

Cet appel renverrait le nombre d'octets écrits (ou zéro si rien n'est écrit) en cas de succès et -1 en cas d'échec. Un numéro d'erreur correct est défini en cas d'échec.

Pour connaître la cause de l'échec, vérifiez avec la variable errno ou la fonction perror ().

Exemples de programmes

Voici quelques exemples de programmes.

Example program 1 - Programme pour écrire et lire deux messages en utilisant pipe.

Algorithme

Step 1 - Créez un tuyau.

Step 2 - Envoyez un message au tuyau.

Step 3 - Récupérez le message du tube et écrivez-le dans la sortie standard.

Step 4 - Envoyez un autre message au tuyau.

Step 5 - Récupérez le message du tube et écrivez-le dans la sortie standard.

Note - La récupération des messages peut également être effectuée après l'envoi de tous les messages.

Source Code: simplepipe.c

#include<stdio.h>
#include<unistd.h>

int main() {
   int pipefds[2];
   int returnstatus;
   char writemessages[2][20]={"Hi", "Hello"};
   char readmessage[20];
   returnstatus = pipe(pipefds);
   
   if (returnstatus == -1) {
      printf("Unable to create pipe\n");
      return 1;
   }
   
   printf("Writing to pipe - Message 1 is %s\n", writemessages[0]);
   write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
   read(pipefds[0], readmessage, sizeof(readmessage));
   printf("Reading from pipe – Message 1 is %s\n", readmessage);
   printf("Writing to pipe - Message 2 is %s\n", writemessages[0]);
   write(pipefds[1], writemessages[1], sizeof(writemessages[0]));
   read(pipefds[0], readmessage, sizeof(readmessage));
   printf("Reading from pipe – Message 2 is %s\n", readmessage);
   return 0;
}

Note- Dans l'idéal, l'état de retour doit être vérifié pour chaque appel système. Pour simplifier le processus, les vérifications ne sont pas effectuées pour tous les appels.

Étapes d'exécution

Compilation

gcc -o simplepipe simplepipe.c

Exécution / Sortie

Writing to pipe - Message 1 is Hi
Reading from pipe – Message 1 is Hi
Writing to pipe - Message 2 is Hi
Reading from pipe – Message 2 is Hell

Example program 2 - Programme pour écrire et lire deux messages via le tube en utilisant les processus parent et enfant.

Algorithme

Step 1 - Créez un tuyau.

Step 2 - Créez un processus enfant.

Step 3 - Le processus parent écrit dans le tube.

Step 4 - Le processus enfant récupère le message du tube et l'écrit dans la sortie standard.

Step 5 - Répétez à nouveau les étapes 3 et 4.

Source Code: pipewithprocesses.c

#include<stdio.h>
#include<unistd.h>

int main() {
   int pipefds[2];
   int returnstatus;
   int pid;
   char writemessages[2][20]={"Hi", "Hello"};
   char readmessage[20];
   returnstatus = pipe(pipefds);
   if (returnstatus == -1) {
      printf("Unable to create pipe\n");
      return 1;
   }
   pid = fork();
   
   // Child process
   if (pid == 0) {
      read(pipefds[0], readmessage, sizeof(readmessage));
      printf("Child Process - Reading from pipe – Message 1 is %s\n", readmessage);
      read(pipefds[0], readmessage, sizeof(readmessage));
      printf("Child Process - Reading from pipe – Message 2 is %s\n", readmessage);
   } else { //Parent process
      printf("Parent Process - Writing to pipe - Message 1 is %s\n", writemessages[0]);
      write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
      printf("Parent Process - Writing to pipe - Message 2 is %s\n", writemessages[1]);
      write(pipefds[1], writemessages[1], sizeof(writemessages[1]));
   }
   return 0;
}

Étapes d'exécution

Compilation

gcc pipewithprocesses.c –o pipewithprocesses

Execution

Parent Process - Writing to pipe - Message 1 is Hi
Parent Process - Writing to pipe - Message 2 is Hello
Child Process - Reading from pipe – Message 1 is Hi
Child Process - Reading from pipe – Message 2 is Hello

Communication bidirectionnelle à l'aide de tuyaux

La communication par canal est considérée comme une communication à sens unique, c'est-à-dire que le processus parent écrit et le processus enfant lit ou vice-versa, mais pas les deux. Cependant, que se passe-t-il si le parent et l'enfant doivent écrire et lire simultanément à partir des canaux, la solution est une communication bidirectionnelle utilisant des canaux. Deux tuyaux sont nécessaires pour établir une communication bidirectionnelle.

Voici les étapes pour parvenir à une communication bidirectionnelle -

Step 1- Créez deux tuyaux. Le premier est que le parent écrive et que l'enfant lise, par exemple pipe1. Le deuxième est que l'enfant écrive et le parent lit, disons pipe2.

Step 2 - Créez un processus enfant.

Step 3 - Fermez les extrémités indésirables car une seule extrémité est nécessaire pour chaque communication.

Step 4 - Fermez les extrémités indésirables dans le processus parent, lisez la fin de pipe1 et écrivez la fin de pipe2.

Step 5 - Fermez les extrémités indésirables dans le processus enfant, écrivez la fin de pipe1 et lisez la fin de pipe2.

Step 6 - Effectuer la communication selon les besoins.

Exemples de programmes

Sample program 1 - Réalisation d'une communication bidirectionnelle à l'aide de tuyaux.

Algorithme

Step 1 - Créez pipe1 pour le processus parent à écrire et le processus enfant à lire.

Step 2 - Créez pipe2 pour le processus enfant à écrire et le processus parent à lire.

Step 3 - Fermez les extrémités indésirables du tuyau du côté parent et du côté enfant.

Step 4 - Processus parent pour écrire un message et processus enfant pour lire et afficher à l'écran.

Step 5 - Processus enfant pour écrire un message et processus parent pour lire et afficher à l'écran.

Source Code: twowayspipe.c

#include<stdio.h>
#include<unistd.h>

int main() {
   int pipefds1[2], pipefds2[2];
   int returnstatus1, returnstatus2;
   int pid;
   char pipe1writemessage[20] = "Hi";
   char pipe2writemessage[20] = "Hello";
   char readmessage[20];
   returnstatus1 = pipe(pipefds1);
   
   if (returnstatus1 == -1) {
      printf("Unable to create pipe 1 \n");
      return 1;
   }
   returnstatus2 = pipe(pipefds2);
   
   if (returnstatus2 == -1) {
      printf("Unable to create pipe 2 \n");
      return 1;
   }
   pid = fork();
   
   if (pid != 0) // Parent process {
      close(pipefds1[0]); // Close the unwanted pipe1 read side
      close(pipefds2[1]); // Close the unwanted pipe2 write side
      printf("In Parent: Writing to pipe 1 – Message is %s\n", pipe1writemessage);
      write(pipefds1[1], pipe1writemessage, sizeof(pipe1writemessage));
      read(pipefds2[0], readmessage, sizeof(readmessage));
      printf("In Parent: Reading from pipe 2 – Message is %s\n", readmessage);
   } else { //child process
      close(pipefds1[1]); // Close the unwanted pipe1 write side
      close(pipefds2[0]); // Close the unwanted pipe2 read side
      read(pipefds1[0], readmessage, sizeof(readmessage));
      printf("In Child: Reading from pipe 1 – Message is %s\n", readmessage);
      printf("In Child: Writing to pipe 2 – Message is %s\n", pipe2writemessage);
      write(pipefds2[1], pipe2writemessage, sizeof(pipe2writemessage));
   }
   return 0;
}

Étapes d'exécution

Compilation

gcc twowayspipe.c –o twowayspipe

Exécution

In Parent: Writing to pipe 1 – Message is Hi
In Child: Reading from pipe 1 – Message is Hi
In Child: Writing to pipe 2 – Message is Hello
In Parent: Reading from pipe 2 – Message is Hello