Невозможно отправить сообщение от дочернего процесса обратно родительскому процессу

#c #pipe #fork

#c #канал #форк

Вопрос:

Я пытаюсь понять, как работают каналы и разветвление. Итак, я написал простую программу, в которой родительский процесс отправляет сообщение дочернему процессу (который отлично работает). Но если я попытаюсь отправить сообщение обратно из дочернего процесса, добавив прокомментированный код, он перестанет работать. И выполнение программы останавливается после вывода «Родительское отправлено: привет».

 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: %sn", msg);
        /*
        fprintf(out, "hi ");
        printf("Child sent: hin"); 
        */
    } 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: hellon");
        /*
        char msg[3];
        fscanf(in, "%s", msg);
        printf("Parent got: %sn", msg);
        */
    }
}
  

И я не могу понять, почему. Что меня больше всего смущает, так это то, почему дочерний процесс не может даже получить сообщение после того, как я изменил код. Кто-нибудь, пожалуйста, может сказать мне, что не так, или направить меня в правильном направлении?

Комментарии:

1. Вероятно, из-за буферизации. Я рекомендую не смешивать stdio / FILE с файловыми дескрипторами / каналами.

2. Вам нужно удалить выходные данные из родительского процесса или закрыть поток от родительского к дочернему. Выходные данные будут полностью буферизованы, и вы не записываете полный буфер.

3. Да — попробуйте поместить fflush(0); после записи родительского процесса, но до того, как он будет прочитан.

4. если вы хотите использовать fscanf("%s", ...) , вам нужно отправить разделитель (например, пробел или n) после слова, чтобы не блокировать fscanf и, конечно, прочитать символ разделителя после, fflush необходим после fwrite . Я ввел в свой ответ 3 способа чтения / записи, затем fread / fwrite (amp; fflush), затем, наконец, небольшое изменение в вашем коде с дополнительным разделителем (amp; fflush)

Ответ №1:

Первое решение, использующее чтение / запись для обмена любым количеством сообщений

Здесь я указываю конец каждого буфера для чтения с помощью n :

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

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

  do {
    if (!read(fd, amp;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], "hin", 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], "hellon", 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 toon", 0);
    }
}
  

Компиляция и выполнение :

 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
  

Также можно использовать fread/fwrite, fflush необходим после fwrite, чтобы не быть заблокированным. К счастью, нет необходимости закрывать канал после отправки, чтобы иметь возможность читать и отвечать, иначе можно обменять только одно сообщение. Я все еще использую n для указания конца отправленного буфера :

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

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

  do {
    if (!fread(amp;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, "hin", 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, "hellon", 0);
    rd(in, 0);

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

    wr(out, "fine toon", 0);
    }
}
  

Компиляция и выполнение:

 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
  

И, наконец, если вы хотите использовать, fscanf("%s", ...) вам нужно отправить разделитель (например, пробел или n) после слова, чтобы не блокировать fscanf и, конечно, также прочитать символ разделителя, fflush необходим после fwrite.

Если я немного изменю вашу программу :

 #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, amp;c);
      printf("Child got: %sn", msg);

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

      fscanf(in ,"%s%c", msg, amp;c);
      printf("Child got: %sn", msg);

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

      fscanf(in ,"%s%c", msg, amp;c);
      printf("Child got: %sn", 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, "hellon");
      fflush(out);
      printf("Parent sent: hellon");

      char msg[16], c;

      fscanf(in, "%s%c", msg, amp;c);
      printf("Parent got: %sn", msg);

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

      fscanf(in, "%s%c", msg, amp;c);
      printf("Parent got: %sn", msg);

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

Компиляция и выполнение :

 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
  

Ответ №2:

fscanf при использовании isspace выполняется чтение до тех пор, пока не будет найден какой-либо пробельный символ (заданный %s ), но вы его не отправляете. So fscanf никогда не возвращается, потому что он ожидает пробел.

Используйте fread и fwrite вместо fscanf и fprintf , и ваши каналы будут работать.

Комментарии:

1. @ThomasPadron-McCarthy, которого нет, если закомментированный код добавлен обратно. Прямо сейчас процесс завершается, закрывая поток; если закомментированный код раскомментирован, оба процесса переходят в fscanf и становятся взаимоблокированными, ожидая, пока один другой отправит символ пробела.

Ответ №3:

Как в родительском, так и в дочернем процессах, когда вы закончите запись в канал, очистите буферы с помощью fflush и закройте каналы, запись в которые заканчивается с помощью close .

Вот немного измененная программа:

 #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: %sn", msg);

        fprintf(out, "hi");
        fflush(out);
        printf("Child sent: hin");
        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: hellon");
        fflush(out);
        close(parent_to_child[1]);

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

Вывод при его запуске:

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