Communication inter-processus - Tuyaux nommés
Les tuyaux étaient destinés à la communication entre les processus connexes. Pouvons-nous utiliser des tuyaux pour une communication de processus indépendante, par exemple, nous voulons exécuter le programme client à partir d'un terminal et le programme serveur à partir d'un autre terminal? La réponse est non. Alors, comment pouvons-nous réaliser des processus de communication indépendants, la réponse simple est Named Pipes. Même si cela fonctionne pour les processus associés, cela ne donne aucun sens à utiliser les canaux nommés pour la communication de processus associée.
Nous avons utilisé un tuyau pour la communication unidirectionnelle et deux tuyaux pour la communication bidirectionnelle. La même condition s'applique-t-elle aux tubes nommés. La réponse est non, nous pouvons utiliser un seul canal nommé qui peut être utilisé pour une communication bidirectionnelle (communication entre le serveur et le client, plus le client et le serveur en même temps) car le canal nommé prend en charge la communication bidirectionnelle.
Un autre nom pour le tube nommé est FIFO (First-In-First-Out). Voyons l'appel système (mknod ()) pour créer un tube nommé, qui est une sorte de fichier spécial.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int mknod(const char *pathname, mode_t mode, dev_t dev);
Cet appel système créerait un fichier spécial ou un nœud de système de fichiers tel qu'un fichier ordinaire, un fichier de périphérique ou un FIFO. Les arguments de l'appel système sont chemin, mode et dev. Le chemin ainsi que les attributs du mode et des informations sur le périphérique. Le chemin d'accès est relatif, si le répertoire n'est pas spécifié, il sera créé dans le répertoire courant. Le mode spécifié est le mode de fichier qui spécifie le type de fichier tel que le type de fichier et le mode de fichier comme mentionné dans les tableaux suivants. Le champ dev sert à spécifier des informations sur le périphérique telles que les numéros de périphérique majeurs et mineurs.
Type de fichier | La description | Type de fichier | La description |
---|---|---|---|
S_IFBLK | bloc spécial | S_IFREG | Fichier régulier |
S_IFCHR | caractère spécial | S_IFDIR | Annuaire |
S_IFIFO | Spécial FIFO | S_IFLNK | Lien symbolique |
Mode fichier | La description | Mode fichier | La description |
---|---|---|---|
S_IRWXU | Lire, écrire, exécuter / rechercher par propriétaire | S_IWGRP | Autorisation d'écriture, groupe |
S_IRUSR | Lire l'autorisation, propriétaire | S_IXGRP | Autorisation d'exécution / recherche, groupe |
S_IWUSR | Autorisation d'écriture, propriétaire | S_IRWXO | Lire, écrire, exécuter / rechercher par d'autres |
S_IXUSR | Autorisation d'exécution / recherche, propriétaire | S_IROTH | Lire l'autorisation, autres |
S_IRWXG | Lire, écrire, exécuter / rechercher par groupe | S_IWOTH | Autorisation d'écriture, autres |
S_IRGRP | Autorisation de lecture, groupe | S_IXOTH | Autorisation d'exécution / recherche, autres |
Le mode fichier peut également être représenté en notation octale telle que 0XYZ, où X représente le propriétaire, Y représente le groupe et Z représente les autres. La valeur de X, Y ou Z peut aller de 0 à 7. Les valeurs de lecture, d'écriture et d'exécution sont respectivement de 4, 2, 1. Si nécessaire en combinaison de lecture, d'écriture et d'exécution, ajoutez les valeurs en conséquence.
Dites, si nous mentionnons, 0640, alors cela signifie lire et écrire (4 + 2 = 6) pour le propriétaire, read (4) pour le groupe et aucune autorisation (0) pour les autres.
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>
int mkfifo(const char *pathname, mode_t mode)
Cette fonction de bibliothèque crée un fichier spécial FIFO, qui est utilisé pour le tube nommé. Les arguments de cette fonction sont le nom de fichier et le mode. Le nom du fichier peut être un chemin absolu ou un chemin relatif. Si le nom du chemin complet (ou le chemin absolu) n'est pas indiqué, le fichier sera créé dans le dossier actuel du processus en cours d'exécution. Les informations sur le mode fichier sont telles que décrites dans l'appel système mknod ().
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 ().
Considérons un programme consistant à exécuter le serveur sur un terminal et à exécuter le client sur un autre terminal. Le programme n'effectuerait qu'une communication à sens unique. Le client accepte l'entrée utilisateur et envoie le message au serveur, le serveur imprime le message sur la sortie. Le processus se poursuit jusqu'à ce que l'utilisateur entre la chaîne «end».
Comprenons cela avec un exemple -
Step 1 - Créez deux processus, un est fifoserver et un autre est fifoclient.
Step 2 - Le processus serveur effectue les opérations suivantes -
Crée un tube nommé (en utilisant l'appel système mknod ()) avec le nom «MYFIFO», s'il n'est pas créé.
Ouvre le tube nommé à des fins de lecture seule.
Ici, créé FIFO avec des autorisations de lecture et d'écriture pour le propriétaire. Lire pour le groupe et aucune autorisation pour les autres.
Attend indéfiniment le message du client.
Si le message reçu du client n'est pas «fin», imprime le message. Si le message est «end», ferme le fifo et termine le processus.
Step 3 - Le processus client effectue les opérations suivantes -
Ouvre le tube nommé à des fins d'écriture uniquement.
Accepte la chaîne de l'utilisateur.
Vérifie si l'utilisateur entre «fin» ou autre que «fin». Dans tous les cas, il envoie un message au serveur. Cependant, si la chaîne est «end», cela ferme le FIFO et termine également le processus.
Se répète indéfiniment jusqu'à ce que l'utilisateur entre la chaîne «fin».
Jetons maintenant un œil au fichier du serveur FIFO.
/* Filename: fifoserver.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "MYFIFO"
int main() {
int fd;
char readbuf[80];
char end[10];
int to_end;
int read_bytes;
/* Create the FIFO if it does not exist */
mknod(FIFO_FILE, S_IFIFO|0640, 0);
strcpy(end, "end");
while(1) {
fd = open(FIFO_FILE, O_RDONLY);
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
to_end = strcmp(readbuf, end);
if (to_end == 0) {
close(fd);
break;
}
}
return 0;
}
Étapes de compilation et d'exécution
Received string: "this is string 1" and length is 16
Received string: "fifo test" and length is 9
Received string: "fifo client and server" and length is 22
Received string: "end" and length is 3
Voyons maintenant l'exemple de code client FIFO.
/* Filename: fifoclient.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "MYFIFO"
int main() {
int fd;
int end_process;
int stringlen;
char readbuf[80];
char end_str[5];
printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
fd = open(FIFO_FILE, O_CREAT|O_WRONLY);
strcpy(end_str, "end");
while (1) {
printf("Enter string: ");
fgets(readbuf, sizeof(readbuf), stdin);
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
end_process = strcmp(readbuf, end_str);
//printf("end_process is %d\n", end_process);
if (end_process != 0) {
write(fd, readbuf, strlen(readbuf));
printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
} else {
write(fd, readbuf, strlen(readbuf));
printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
close(fd);
break;
}
}
return 0;
}
Prenons un à la sortie d'arrivée.
Étapes de compilation et d'exécution
FIFO_CLIENT: Send messages, infinitely, to end enter "end"
Enter string: this is string 1
Sent string: "this is string 1" and string length is 16
Enter string: fifo test
Sent string: "fifo test" and string length is 9
Enter string: fifo client and server
Sent string: "fifo client and server" and string length is 22
Enter string: end
Sent string: "end" and string length is 3
Communication bidirectionnelle à l'aide de canaux nommés
La communication entre les tuyaux est censée être unidirectionnelle. Les tuyaux étaient limités à une communication unidirectionnelle en général et ont besoin d'au moins deux tuyaux pour une communication bidirectionnelle. Les tuyaux sont destinés uniquement aux processus interdépendants. Les tuyaux ne peuvent pas être utilisés pour la communication de processus non liés, par exemple, si nous voulons exécuter un processus à partir d'un terminal et un autre processus à partir d'un autre terminal, ce n'est pas possible avec des tuyaux. Avons-nous un moyen simple de communiquer entre deux processus, disons des processus non liés de manière simple? La réponse est oui. Le canal nommé est destiné à la communication entre deux ou plusieurs processus indépendants et peut également avoir une communication bidirectionnelle.
Déjà, nous avons vu la communication unidirectionnelle entre les canaux nommés, c'est-à-dire les messages du client vers le serveur. Voyons maintenant la communication bidirectionnelle, c'est-à-dire que le client envoie un message au serveur et le serveur reçoit le message et renvoie un autre message au client en utilisant le même tube nommé.
Voici un exemple -
Step 1 - Créez deux processus, l'un est fifoserver_twoway et un autre est fifoclient_twoway.
Step 2 - Le processus serveur effectue les opérations suivantes -
Crée un tube nommé (en utilisant la fonction de bibliothèque mkfifo ()) avec le nom «fifo_twoway» dans le répertoire / tmp, s'il n'est pas créé.
Ouvre le tube nommé à des fins de lecture et d'écriture.
Ici, créé FIFO avec des autorisations de lecture et d'écriture pour le propriétaire. Lire pour le groupe et aucune autorisation pour les autres.
Attend indéfiniment un message du client.
Si le message reçu du client n'est pas «fin», imprime le message et inverse la chaîne. La chaîne inversée est renvoyée au client. Si le message est «end», ferme le fifo et termine le processus.
Step 3 - Le processus client effectue les opérations suivantes -
Ouvre le tube nommé à des fins de lecture et d'écriture.
Accepte la chaîne de l'utilisateur.
Vérifie si l'utilisateur entre «fin» ou autre que «fin». Dans tous les cas, il envoie un message au serveur. Cependant, si la chaîne est «end», cela ferme le FIFO et termine également le processus.
Si le message n'est pas envoyé comme «fin», il attend le message (chaîne inversée) du client et imprime la chaîne inversée.
Se répète indéfiniment jusqu'à ce que l'utilisateur entre la chaîne «fin».
Jetons maintenant un coup d'œil à l'exemple de code de serveur FIFO.
/* Filename: fifoserver_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "/tmp/fifo_twoway"
void reverse_string(char *);
int main() {
int fd;
char readbuf[80];
char end[10];
int to_end;
int read_bytes;
/* Create the FIFO if it does not exist */
mkfifo(FIFO_FILE, S_IFIFO|0640);
strcpy(end, "end");
fd = open(FIFO_FILE, O_RDWR);
while(1) {
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("FIFOSERVER: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
to_end = strcmp(readbuf, end);
if (to_end == 0) {
close(fd);
break;
}
reverse_string(readbuf);
printf("FIFOSERVER: Sending Reversed String: \"%s\" and length is %d\n", readbuf, (int) strlen(readbuf));
write(fd, readbuf, strlen(readbuf));
/*
sleep - This is to make sure other process reads this, otherwise this
process would retrieve the message
*/
sleep(2);
}
return 0;
}
void reverse_string(char *str) {
int last, limit, first;
char temp;
last = strlen(str) - 1;
limit = last/2;
first = 0;
while (first < last) {
temp = str[first];
str[first] = str[last];
str[last] = temp;
first++;
last--;
}
return;
}
Étapes de compilation et d'exécution
FIFOSERVER: Received string: "LINUX IPCs" and length is 10
FIFOSERVER: Sending Reversed String: "sCPI XUNIL" and length is 10
FIFOSERVER: Received string: "Inter Process Communication" and length is 27
FIFOSERVER: Sending Reversed String: "noitacinummoC ssecorP retnI" and length is 27
FIFOSERVER: Received string: "end" and length is 3
Jetons maintenant un coup d'œil à l'exemple de code client FIFO.
/* Filename: fifoclient_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "/tmp/fifo_twoway"
int main() {
int fd;
int end_process;
int stringlen;
int read_bytes;
char readbuf[80];
char end_str[5];
printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
fd = open(FIFO_FILE, O_CREAT|O_RDWR);
strcpy(end_str, "end");
while (1) {
printf("Enter string: ");
fgets(readbuf, sizeof(readbuf), stdin);
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
end_process = strcmp(readbuf, end_str);
//printf("end_process is %d\n", end_process);
if (end_process != 0) {
write(fd, readbuf, strlen(readbuf));
printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("FIFOCLIENT: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
} else {
write(fd, readbuf, strlen(readbuf));
printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
close(fd);
break;
}
}
return 0;
}
Étapes de compilation et d'exécution
FIFO_CLIENT: Send messages, infinitely, to end enter "end"
Enter string: LINUX IPCs
FIFOCLIENT: Sent string: "LINUX IPCs" and string length is 10
FIFOCLIENT: Received string: "sCPI XUNIL" and length is 10
Enter string: Inter Process Communication
FIFOCLIENT: Sent string: "Inter Process Communication" and string length is 27
FIFOCLIENT: Received string: "noitacinummoC ssecorP retnI" and length is 27
Enter string: end
FIFOCLIENT: Sent string: "end" and string length is 3