【问题标题】:Unable to send a message from a child process back to the parent process无法将消息从子进程发送回父进程
【发布时间】:2019-03-05 21:42:57
【问题描述】:

我试图了解管道和分叉是如何工作的。所以我写了一个简单的程序,父进程向子进程发送消息(工作正常)。但是,如果我尝试通过添加注释代码从子进程发回消息,它就会停止工作。并且程序的执行在输出“Parent sent: hello”后停止。

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);
        */
    }
}

我不知道为什么。最让我困惑的是为什么我修改代码后子进程甚至无法收到消息。谁能告诉我出了什么问题,或者指引我正确的方向?

【问题讨论】:

  • 可能是因为缓冲。我建议不要将 stdio / FILE 与文件描述符 / 管道混合。
  • 您需要刷新父级的输出,或者关闭从父级到子级的流。输出将被完全缓冲,并且您不会将缓冲区写满。
  • 是的 - 尝试将 fflush(0); 放在父级写入之后,但在读取之前。
  • 如果您想使用fscanf("%s", ...),您需要在单词之后发送一个分隔符(例如空格或 \n)以不阻止 fscanf 并且当然要阅读后面的分隔符,fwrite 后面需要fflush。我在我的答案中输入了 3 种方式 read/write 然后 fread/fwrite(&fflush) 然后最后用额外的分隔符 (&fflush) 对你的代码进行一些更改

标签: c pipe fork


【解决方案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, &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);
    }
}

编译和执行:

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/fwritefflush是必须在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(&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);
    }
}

编译和执行:

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, &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");
    }
}

编译和执行:

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 在您使用%s 时一直读取直到找到一些空白字符(由isspace 给出),但您没有发送。所以fscanf 永远不会返回,因为它正在等待空格。

    使用freadfwrite 而不是fscanffprintf,你的管道就可以工作了。

    【讨论】:

    • @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: %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);
        }
    }
    

    运行时的输出:

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

    【讨论】:

      猜你喜欢
      • 2014-10-08
      • 1970-01-01
      • 2016-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多