2
votes

Impossible de renvoyer un message d'un processus enfant au processus parent

J'essaie de comprendre comment fonctionnent les tuyaux et les fourches. J'ai donc écrit un programme simple où un processus parent envoie un message au processus enfant (ce qui fonctionne très bien). Mais si j'essaye de renvoyer un message à partir du processus enfant en ajoutant le code commenté, cela cesse de fonctionner. Et l'exécution du programme s'arrête après avoir sorti "Parent envoyé: bonjour".

int main() {
    int child_to_parent[2];
    int parent_to_child[2];
    pipe(child_to_parent);
    pipe(parent_to_child);

    pid_t id = fork();

    if (id == 0) {
        close(parent_to_child[1]);
        close(child_to_parent[0]);
        FILE* out = fdopen(child_to_parent[1], "w");
        FILE* in = fdopen(parent_to_child[0], "r");

        char msg[6];
        fscanf(in ,"%s", msg);
        printf("Child got: %s\n", msg);
        /*
        fprintf(out, "hi ");
        printf("Child sent: hi\n"); 
        */
    } else {
        close(parent_to_child[0]);
        close(child_to_parent[1]);
        FILE* in = fdopen(child_to_parent[0], "r");
        FILE* out = fdopen(parent_to_child[1], "w");

        fprintf(out, "hello");
        printf("Parent sent: hello\n");
        /*
        char msg[3];
        fscanf(in, "%s", msg);
        printf("Parent got: %s\n", msg);
        */
    }
}

Et je ne comprends pas pourquoi. Ce qui me trouble le plus, c'est pourquoi le processus enfant ne peut même pas recevoir le message après avoir modifié le code. Quelqu'un pourrait-il me dire ce qui ne va pas ou me diriger dans la bonne direction?


4 commentaires

Probablement à cause de la mise en mémoire tampon. Je recommande de ne pas mélanger stdio / FILE avec des descripteurs de fichiers / tubes.


Vous devez vider la sortie du parent ou fermer le flux du parent à l'enfant. La sortie sera entièrement mise en mémoire tampon et vous n'écrivez pas une mémoire tampon pleine.


Oui - essayez de mettre fflush (0); après l'écriture du parent, mais avant sa lecture.


si vous voulez utiliser fscanf ("% s", ...) vous devez envoyer un séparateur (un espace ou \ n par exemple) après le mot pour ne pas bloquer fscanf et bien sûr pour lire le caractère séparateur après, le fflush est nécessaire après le fwrite . J'ai mis 3 manières dans ma réponse en lecture / écriture puis fread / fwrite (& fflush) puis enfin un petit changement de votre code avec le séparateur supplémentaire (& fflush)


3 Réponses :


0
votes

Première solution utilisant la lecture / écriture pour échanger un nombre quelconque de messages

Ici, j'indique la fin de chaque buffer à lire par un \ n:

pi@raspberrypi:/tmp $ gcc -pedantic -Wextra ppp.c
pi@raspberrypi:/tmp $ ./a.out
Parent sent: hello
Child got: hello
Child sent: hi
Parent got: hi
Parent sent: how-are-you?
Child got: how-are-you?
Child sent: fine,you?
Parent got: fine,you?
Parent sent: fine-too
Child got: fine-too

Compilation et exécution:

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

int main() {
    int child_to_parent[2];
    int parent_to_child[2];
    pipe(child_to_parent);
    pipe(parent_to_child);

    pid_t id = fork();

    if (id == 0) {
      close(parent_to_child[1]);
      close(child_to_parent[0]);
      FILE* out = fdopen(child_to_parent[1], "w");
      FILE* in = fdopen(parent_to_child[0], "r");
      char msg[16], c;

      fscanf(in ,"%s%c", msg, &c);
      printf("Child got: %s\n", msg);

      fprintf(out, "hi ");
      fflush(out);
      printf("Child sent: hi\n"); 

      fscanf(in ,"%s%c", msg, &c);
      printf("Child got: %s\n", msg);

      fprintf(out, "fine,you? ");
      fflush(out);
      printf("Child sent: fine,you?\n"); 

      fscanf(in ,"%s%c", msg, &c);
      printf("Child got: %s\n", msg);
    } else {
      close(parent_to_child[0]);
      close(child_to_parent[1]);
      FILE* in = fdopen(child_to_parent[0], "r");
      FILE* out = fdopen(parent_to_child[1], "w");

      fprintf(out, "hello\n");
      fflush(out);
      printf("Parent sent: hello\n");

      char msg[16], c;

      fscanf(in, "%s%c", msg, &c);
      printf("Parent got: %s\n", msg);

      fprintf(out, "how-are-you? ");
      fflush(out);
      printf("Parent sent: how-are-you?\n");

      fscanf(in, "%s%c", msg, &c);
      printf("Parent got: %s\n", msg);

      fprintf(out, "fine-too ");
      fflush(out);
      printf("Parent sent: fine-too\n");
    }
}

Il est également possible d'utiliser fread / fwrite , fflush est nécessaire après fwrite pour ne pas être bloqué. Il n'est heureusement pas nécessaire de fermer le pipe après l'envoi pour pouvoir lire et répondre, sinon un seul message peut être échangé. J'utilise toujours \ n pour indiquer la fin du tampon envoyé:

pi@raspberrypi:/tmp $ gcc -pedantic -Wextra pp.c
pi@raspberrypi:/tmp $ ./a.out
Parent sent:hello
Child got:hello
Child sent:hi
Parent got:hi
Parent sent:how are you ?
Child got:how are you ?
Child sent:fine, and you ?
Parent got:fine, and you ?
Parent sent:fine too
Child got:fine too

Compilation et exécution:

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

void rd(FILE * fd, int child)
{
  char c;
  int first = 1;

  do {
    if (!fread(&c, 1, 1, fd))
      break;
    if (first) {
      printf("%s got:", (child) ? "Child" : "Parent");
      first = 0;
    }
    putchar(c);
  } while (c != '\n');
}

void wr(FILE *  fd, const char * msg, int child)
{
  fwrite(msg, strlen(msg), 1, fd);
  fflush(fd);
  printf("%s sent:%s", (child) ? "Child" : "Parent", msg);
}

int main() {
    int child_to_parent[2];
    int parent_to_child[2];
    pipe(child_to_parent);
    pipe(parent_to_child);

    pid_t id = fork();

    if (id == 0) {
        close(parent_to_child[1]);
        close(child_to_parent[0]);
        FILE* out = fdopen(child_to_parent[1], "w");
        FILE* in = fdopen(parent_to_child[0], "r");

    rd(in, 1);
    wr(out, "hi\n", 1);

    rd(in, 1);
    wr(out, "fine, and you ?\n", 1);

    rd(in, 1);
    } else {
        close(parent_to_child[0]);
        close(child_to_parent[1]);
        FILE* in = fdopen(child_to_parent[0], "r");
        FILE* out = fdopen(parent_to_child[1], "w");

    wr(out, "hello\n", 0);
    rd(in, 0);

    wr(out, "how are you ?\n", 0);
    rd(in, 0);

    wr(out, "fine too\n", 0);
    }
}

Et enfin si vous voulez utiliser fscanf ("% s", ...) vous devez envoyer un séparateur (un espace ou \ n par exemple) après le mot pour ne pas bloquer fscanf et bien sûr pour lire également le caractère séparateur , le fflush est nécessaire après le fwrite .

Si je change un peu votre programme:

pi@raspberrypi:/tmp $ gcc -pedantic -Wextra p.c
pi@raspberrypi:/tmp $ ./a.out 
Parent sent:hello
Child got:hello
Child sent:hi
Parent got:hi
Parent sent:how are you ?
Child got:how are you ?
Child sent:fine, and you ?
Parent got:fine, and you ?
Parent sent:fine too
Child got:fine too

Compilation et exécution:

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

void rd(int fd, int child)
{
  char c;
  int first = 1;

  do {
    if (!read(fd, &c, 1))
      break;
    if (first) {
      printf("%s got:", (child) ? "Child" : "Parent");
      first = 0;
    }
    putchar(c);
  } while (c != '\n');
}

void wr(int fd, const char * msg, int child)
{
  write(fd, msg, strlen(msg));
  printf("%s sent:%s", (child) ? "Child" : "Parent", msg);
}

int main() {
    int child_to_parent[2];
    int parent_to_child[2];

    pipe(child_to_parent);
    pipe(parent_to_child);

    pid_t id = fork();

    if (id == 0) {
      close(parent_to_child[1]);
      close(child_to_parent[0]);

      rd(parent_to_child[0], 1);
      wr(child_to_parent[1], "hi\n", 1);

      rd(parent_to_child[0], 1);
      wr(child_to_parent[1], "fine, and you ?\n", 1);

      rd(parent_to_child[0], 1);
    } else {
      close(parent_to_child[0]);
      close(child_to_parent[1]);

      wr(parent_to_child[1], "hello\n", 0);
      rd(child_to_parent[0], 0);

      wr(parent_to_child[1], "how are you ?\n", 0);
      rd(child_to_parent[0], 0);

      wr(parent_to_child[1], "fine too\n", 0);
    }
}


0 commentaires

0
votes

fscanf lit jusqu'à ce qu'un espace blanc (comme indiqué par isspace ) soit trouvé lorsque vous utilisez % s , mais vous n'envoyez pas une. Donc fscanf ne revient jamais, car il attend l'espace blanc.

Utilisez fread et fwrite au lieu de fscanf et fprintf et vos tubes fonctionneront.


1 commentaires

@ ThomasPadron-McCarthy Ce n'est pas le cas si le code commenté est de nouveau ajouté. En ce moment, le processus se termine, fermant le flux; si le code commenté n'est pas commenté, les deux processus vont dans fscanf et deviennent bloqués, attendant l'un de l'autre pour envoyer un caractère d'espacement.



0
votes

Dans le parent et dans l'enfant, lorsque vous avez fini d'écrire dans le tube, videz les tampons avec fflush et fermez le tube. > Voici un programme légèrement modifié:

Parent sent: hello
Parent gonna read...
Child gonna read...
Child got: hello
Child sent: hi
Parent got: hi

Sortie lorsque je l'exécute:

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

FILE *fdopen(int fd, const char *mode);

int main() {
    int child_to_parent[2];
    int parent_to_child[2];
    pipe(child_to_parent);
    pipe(parent_to_child);

    pid_t id = fork();

    if (id == 0) {
        close(parent_to_child[1]);
        close(child_to_parent[0]);
        FILE* out = fdopen(child_to_parent[1], "w");
        FILE* in = fdopen(parent_to_child[0], "r");

        char msg[6];
        printf("Child gonna read...\n");
        fscanf(in ,"%s", msg);
        printf("Child got: %s\n", msg);

        fprintf(out, "hi");
        fflush(out);
        printf("Child sent: hi\n");
        close(child_to_parent[1]);
    } else {
        close(parent_to_child[0]);
        close(child_to_parent[1]);
        FILE* in = fdopen(child_to_parent[0], "r");
        FILE* out = fdopen(parent_to_child[1], "w");

        fprintf(out, "hello");
        printf("Parent sent: hello\n");
        fflush(out);
        close(parent_to_child[1]);

        char msg[3];
        printf("Parent gonna read...\n");
        fscanf(in, "%s", msg);
        printf("Parent got: %s\n", msg);
    }
}


0 commentaires