Bonjour je vois bien qu'un fils peut communiquer avec le père via tube mais est ce que deux fils peuvent communiquer entre eux via tube ? un truc comme ça
Bonjour je vois bien qu'un fils peut communiquer avec le père via tube mais
1) est ce que deux fils peuvent communiquer entre eux via tube ?
2) un truc comme ça
fils1--->fils2--->fils3---->pére---->fils1
1) oui, sans problème.
le père crée le tube
il "forke" deux fils, qui en héritent
les fils peuvent communiquer (pendant ce temps, le père peut fermer le tube dont il n'a pas l'usage)
Pour essayer : écrire un programme minimum dans lequel
le père crée un tube
il lance un fils qui fait quelques tours de boucle avec attente quelques secondes, envoi d'un message "hello" dans le tube, puis s'arrête.
il lance un autre fils qui affiche les messages qu'il reçoit, jusqu'à fin des données.
et il attend que les fils aient terminé.
Conseil : ne pas oublier de fermer les bouts de tubes dont on ne se sert pas.
(solution plus tard, quand vous aurez avancé)
2) Comprends pas le schéma. Il y a un tube qui passe à travers des processus ? Ou c'est autant de tubes ?
- Edité par michelbillaud il y a 16 minutes
bonsoir, oui enfaite le schéma c'est bien 4 tubes différents le but c'est que le fils 1 envoie un message qui va être modifié à la traversé de chaque fils via les tubes afin d'arriver au père
- Edité par amstaTargaryen 7 novembre 2022 à 19:36:15
bonsoir, oui enfaite le schéma c'est bien 4 tubes différents le but c'est que le fils 1 envoie un message qui va être modifié à la traversé de chaque fils via les tubes afin d'arriver au père
Bref, le problème de fond, c'est de faire communiquer 2 processus par un tube dont ils ont hérité.
J'ai écrit le code suivant pour illustrer le principe avec pipe (_pipe sur Windows): Ici, le fils 0 représente le père. Pour boucler sur N fils, il faut N+1 pipe.
Chaque fils recevra deux pipe, celui avec son prédécesseur et celui avec son successeur.
- #include <stdio.h> #include <windows.h> #include <fcntl.h> #define np 4 int main(void) { int pipes[np][2]; // np pipes. for(int p = 0; p < np; p++) { _pipe(pipes[p], 1024, _O_TEXT); printf("pipe %d, fils %d vers fils %d, input=%d, output=%d\n", p, p, (p+1)%np, pipes[p][0], pipes[p][1]); } // Procédure de transfert: for(int p = 0; p < np; p++) { printf("Le fils %d lira sur %d et écrira sur %d\n", p, pipes[(p-1+np)%np][0], pipes[p][1]); } return 0; } - pipe 0, fils 0 vers fils 1, input=3, output=4 pipe 1, fils 1 vers fils 2, input=5, output=6 pipe 2, fils 2 vers fils 3, input=7, output=8 pipe 3, fils 3 vers fils 0, input=9, output=10 Le fils 0 lira sur 9 et écrira sur 4 Le fils 1 lira sur 3 et écrira sur 6 Le fils 2 lira sur 5 et écrira sur 8 Le fils 3 lira sur 7 et écrira sur 10
- Edité par PierrotLeFou 8 novembre 2022 à 5:06:43
Le Tout est souvent plus grand que la somme de ses parties.
Bon alors voila un exemple de partage de tuyau par deux processus fils, un écrivain qui y met des messages, un fils qui lit ce qu'il y trouve.
Attention, Seuls Ceux Qui Ont Un QI de 160 Vont Voir Qu'il Y A Un Problème
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
void ecrire_dans(int out)
{
for (int i = 1; i <= 5; i++) {
sleep(1);
char message[100];
size_t len = sprintf(message, "Envoi numéro %d", i);
printf("Ecrivain %d> envoi de \"%s\"\n", i, message);
write(out, message, len);
}
}
void lire_sur(int in)
{
char message[100 + 1];
int nb_received = 0;
while (true) {
size_t len = read(in, message, 100);
if (len <= 0) break;
nb_received ++;
message[len] = '\0';
printf("Lecteur %d > réception de \"%s\"\n", nb_received, message);
// sleep(2); // fait dérailler le programme.
}
printf("Lecteur> fin des données\n");
}
int main()
{
printf("DEBUT\n");
int tube[2];
pipe(tube);
printf("Main > lancement écrivain\n");
pid_t ecrivain = fork();
if (ecrivain == 0) {
close(tube[0]);
ecrire_dans(tube[1]);
exit(0);
}
close(tube[1]); // le père et le lecteur n'en ont pas besoin
printf("Main > lancement lecteur\n");
pid_t lecteur = fork();
if (lecteur == 0) {
lire_sur(tube[0]);
exit(0);
}
close(tube[0]); // le père n'en a pas besoin
printf("Main> en attente\n");
wait(NULL);
printf("Main> Un des processus fils s'est arrếté\n");
wait(NULL);
printf("Main> L'autre processus fils s'est arrêté\n");
printf("FIN\n");
return EXIT_SUCCESS;
}
Quand ça s'exécute, ça fait
cc -std=c17 -Wall -Wextra -pedantic -Werror -Wno-unused -D_XOPEN_SOURCE=700 -g prog.c -o prog
./prog
DEBUT
Main > lancement écrivain
Main > lancement lecteur
Main> en attente
Ecrivain 1> envoi de "Envoi numéro 1"
Lecteur 1 > réception de "Envoi numéro 1"
Ecrivain 2> envoi de "Envoi numéro 2"
Lecteur 2 > réception de "Envoi numéro 2"
Ecrivain 3> envoi de "Envoi numéro 3"
Lecteur 3 > réception de "Envoi numéro 3"
Ecrivain 4> envoi de "Envoi numéro 4"
Lecteur 4 > réception de "Envoi numéro 4"
Ecrivain 5> envoi de "Envoi numéro 5"
Lecteur 5 > réception de "Envoi numéro 5"
Lecteur> fin des données
Main> Un des processus fils s'est arrêté
Main> L'autre processus fils s'est arrêté
FIN
Par contre, ça repose sur le fait que le 'lecteur" a le temps de consommer les messages un par un, à cause de la temporisation dans l'écrivain.
Si on décommente la ligne
sleep(2); // fait dérailler le programme.
C'est plus pareil
cc -std=c17 -Wall -Wextra -pedantic -Werror -Wno-unused -D_XOPEN_SOURCE=700 -g prog.c -o prog
./prog
DEBUT
Main > lancement écrivain
Main > lancement lecteur
Main> en attente
Ecrivain 1> envoi de "Envoi numéro 1"
Lecteur 1 > réception de "Envoi numéro 1"
Ecrivain 2> envoi de "Envoi numéro 2"
Lecteur 2 > réception de "Envoi numéro 2"
Ecrivain 3> envoi de "Envoi numéro 3"
Ecrivain 4> envoi de "Envoi numéro 4"
Lecteur 3 > réception de "Envoi numéro 3Envoi numéro 4" // OOPS !
Ecrivain 5> envoi de "Envoi numéro 5"
Main> Un des processus fils s'est arrêté
Lecteur 4 > réception de "Envoi numéro 5"
Lecteur> fin des données
Main> L'autre processus fils s'est arrêté
FIN
ça peut se désynchroniser. En effet l'écrivain a eu le temps d'envoyer 2 messages avant que le lecteur (qui fait des pauses) se remette à lire.
Donc les 2 messages 3 et 4 se sont accumulés dans le tuyau, et le lecteur les a lus en bloc, comme si c'était un seul.
(Il pourrait arriver aussi - avec d'autres valeurs de délai - que les messages s'accumulent au point de remplir le pipe. Du coup, l'écrivain sera bloqué.)
Conclusion : ceci est une démo de partage de tuyau par deux processus fils, mais pas un modèle à suivre bêtement. Si on veut une synchro entre lecteur et écrivain pour travailler message par message, il ne faut pas s'y prendre comme ça.
- Edité par michelbillaud 9 novembre 2022 à 12:47:48
Comme je n'ai qu'un QI de 40 avec 3 ou 4 neurones ... j'ai fait le test suivant: sprintf ne compte pas le '\0' de fin de chaîne. On peut bloquer l'écrivain (sleep) ou avoir un code de fin de message que le lecteur devrait savoir interpréter. Dans certains protocoles, l'écrivain envoie le nombre d'octets à transmettre au début du message. - #include <stdio.h> int main(){ char msg[100] = { "************************" }; size_t l = sprintf(msg, "Number %d", 4); printf("%d, %s", l, msg); } - 8, Number 4
Le Tout est souvent plus grand que la somme de ses parties.
On peut s'y prendre comme ça. Le but de ma remarque, c'était de souligner qu'un pipe c'était un flux d'octets et non une séquence de messages. Même si il y a quelques propriétés qui laissent éventuellement croire le contraire :
L'écriture par write dans un tuyau est atomique : soit il y reste assez de place pour y mettre tout qui ce qu'on indique par drive, soit l'écrivain est bloqué en attendant.
Côté lecteur, il est possible de lire ce qui est présent dans le pipe, même si c'est une taille inférieure au tampon indiqué par read, et que la fin n'est pas atteinte (contrairement aux fichiers)
C'est ce qui fait marcher l'exemple. Par miracle. Parce que la temporisation fait qu'on est PRESQUE sûrs qu'il n'y a jamais qu'un message à la fois dans le tuyau.
La solution la plus simple si on veut transmettre proprement (sans compter sur des temporisations) des messages plutôt qu'un flux : messages de même taille.
Sinon, présence dans chaque message d'une entête (de taille fixe) indiquant la taille des données qui suivent. Écriture en un seul write (en tête + donnees) pour être sûr que les données sont dans le pipe juste après que le lecteur a lu l'entête....
Illustration, ce coup-ci avec 3 processus fils qui envoient chacun 5 messages, avec des temporisations aléatoires. Temporisation aussi du côté du lecteur.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stddef.h>
struct Entete {
ssize_t longueur_donnees;
};
struct Message {
struct Entete entete;
char donnees[100];
};
void ecrire_dans(int id_ecrivain, int out)
{
srand(id_ecrivain);
for (int num_message = 1; num_message <= 5; num_message++) {
sleep(rand() % 4);
struct Message message;
size_t nb_chars = sprintf(message.donnees,
"message numéro %d envoyé par écrivain %d",
num_message, id_ecrivain);
message.entete.longueur_donnees = nb_chars;
printf("Ecrivains> \"%s\"\n", message.donnees);
write(out, & message, offsetof(struct Message, donnees) + nb_chars);
}
}
void lire_sur(int in)
{
struct Entete entete;
char donnees[100 + 1];
int nb_received = 0;
while (true) {
// lecture entete si possible, sinon fin
if (read(in, & entete, sizeof(entete)) <= 0) break;
// lecture données
read(in, donnees, entete.longueur_donnees);
nb_received ++;
donnees[entete.longueur_donnees] = '\0';
printf("Lecteur %d > réception de \"%s\"\n", nb_received, donnees);
sleep(rand() % 3);
}
printf("Lecteur> fin des données\n");
}
int main()
{
const int nb_ecrivains = 3;
printf("DEBUT\n");
int tube[2];
pipe(tube);
for (int id = 1; id <= nb_ecrivains; id++) {
printf("Main > lancement écrivain %d\n", id);
pid_t ecrivain = fork();
if (ecrivain == 0) {
close(tube[0]);
ecrire_dans(id, tube[1]);
exit(0);
}
}
close(tube[1]); // le père et le lecteur n'en ont pas besoin
printf("Main > lancement lecteur\n");
pid_t lecteur = fork();
if (lecteur == 0) {
lire_sur(tube[0]);
exit(0);
}
close(tube[0]); // le père n'en a pas besoin
printf("Main> en attente\n");
wait(NULL);
for (int i = 1; i <= nb_ecrivains + 1; i++) { // lecteur + ecrivains
printf("Main> Arret processus fils %i/%i\n", i, nb_ecrivains+1);
wait(NULL);
}
printf("FIN\n");
return EXIT_SUCCESS;
}
Trace d'une exécution
$ cc -std=c17 -Wall -Wextra -pedantic -Werror -Wno-unused -D_XOPEN_SOURCE=700 -g prog.c -o prog
$ ./prog
DEBUT
Main > lancement écrivain 1
Main > lancement écrivain 2
Main > lancement écrivain 3
Main > lancement lecteur
Main> en attente
Ecrivains> "message numéro 1 envoyé par écrivain 2"
Ecrivains> "message numéro 1 envoyé par écrivain 3"
Lecteur 1 > réception de "message numéro 1 envoyé par écrivain 2"
Ecrivains> "message numéro 1 envoyé par écrivain 1"
Ecrivains> "message numéro 2 envoyé par écrivain 3"
Lecteur 2 > réception de "message numéro 1 envoyé par écrivain 3"
Ecrivains> "message numéro 3 envoyé par écrivain 3"
Ecrivains> "message numéro 4 envoyé par écrivain 3"
Lecteur 3 > réception de "message numéro 1 envoyé par écrivain 1"
Lecteur 4 > réception de "message numéro 2 envoyé par écrivain 3"
Ecrivains> "message numéro 5 envoyé par écrivain 3"
Main> Arret processus fils 1/4
Ecrivains> "message numéro 2 envoyé par écrivain 2"
Ecrivains> "message numéro 2 envoyé par écrivain 1"
Ecrivains> "message numéro 3 envoyé par écrivain 2"
Lecteur 5 > réception de "message numéro 3 envoyé par écrivain 3"
Ecrivains> "message numéro 3 envoyé par écrivain 1"
Lecteur 6 > réception de "message numéro 4 envoyé par écrivain 3"
Ecrivains> "message numéro 4 envoyé par écrivain 2"
Lecteur 7 > réception de "message numéro 5 envoyé par écrivain 3"
Ecrivains> "message numéro 4 envoyé par écrivain 1"
Ecrivains> "message numéro 5 envoyé par écrivain 2"
Main> Arret processus fils 2/4
Lecteur 8 > réception de "message numéro 2 envoyé par écrivain 2"
Lecteur 9 > réception de "message numéro 2 envoyé par écrivain 1"
Lecteur 10 > réception de "message numéro 3 envoyé par écrivain 2"
Ecrivains> "message numéro 5 envoyé par écrivain 1"
Main> Arret processus fils 3/4
Lecteur 11 > réception de "message numéro 3 envoyé par écrivain 1"
Lecteur 12 > réception de "message numéro 4 envoyé par écrivain 2"
Lecteur 13 > réception de "message numéro 4 envoyé par écrivain 1"
Lecteur 14 > réception de "message numéro 5 envoyé par écrivain 2"
Lecteur 15 > réception de "message numéro 5 envoyé par écrivain 1"
Lecteur> fin des données
Main> Arret processus fils 4/4
FIN
On en a bien reçu 15 = 3 * 5 : bingo.
- Edité par michelbillaud 10 novembre 2022 à 9:46:17
communication interprocess
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Le Tout est souvent plus grand que la somme de ses parties.
Le Tout est souvent plus grand que la somme de ses parties.