我是管道技术的新手,一直在尝试创建一对管道,以允许子进程写入父进程,并使父进程进行回传。一位 parent 带最多4个 child 。子程序将与exec变成另一个程序。

我在做什么:

从父进程到子进程的写入。当我读入子程序的stdin时,它将收到我从父程序写的内容。

目标:

创建一个纸牌游戏,其中 parent 与每个单独的客户交谈(子进程),并向其提供所有 Action 和信息,从其标准输出到 child 的标准输入。各个子进程在其标准输出上返回其 Action ,由主父读取。游戏的 Action 完全由顺序决定,而不是玩家决定。所以这是一个机器人游戏。

我遇到的问题:

我不确定如何获取它,以便 parent 可以通过文件流读取 child 的标准输出。当我尝试设置子行的读数时,代码似乎停止工作。甚至 child 也不能从 parent 那里读(似乎已经停止了现在注释掉的将 child 设置为 parent 的留置权)。

我也不确定如何“等待”直到出现某种情况。就像,开始时,玩家必须向 parent 发送“准备就绪”消息,让他们知道自己正在工作。当我从 child 那里发送“就绪”消息后,如何无限期“等待”,直到出现下一条消息?

我不确定是否正确设置了管道。有人可以提供有关如何使用通讯管道的指导,并在下面确认我的逻辑吗?

我为让 parent 给 child 写的书是:

  • 首先创建管道
  • 将父进程 fork 到另一个进程(子进程)
  • 将管道的入口连接到父级的stdout,并使用dup2关闭父级的读取端,然后关闭
  • 将管道的输出连接到 child 的stdin,并使用dup2关闭 child 的书写部分,然后关闭
  • 使用fdopen()从文件描述符中获取文件流,然后打印到该文件流。
  • 现在,子进程stdin就是您从父进程打印到stdout的任何东西。

  • 这样对吗?我尝试将这种逻辑应用于 child 的 parent ,但要扭转它。
  • 将in管道连接到读取的文件流,这是子程序从其stdout写入的位置。
  • 将out管道连接到父级读取的读取流。
    void start_child_process(Game *game, int position) {
      int child2Parent[2];
      int parent2Child[2];
    
      if (pipe(parent2Child)) {
          printf("PIPE FAIL!\n");
      }
      if (pipe(child2Parent)) {
          printf("PIPE FAIL!\n");
      }
    
      pid_t pid = fork();
      game->readStream[position] = fdopen(child2Parent[0], "r");
      game->writeStream[position] = fdopen(parent2Child[1], "w");
    
      if (pid) { // Parent
          // Write from parent to child
          close(parent2Child[0]);
          dup2(fileno(game->writeStream[position]), STDOUT_FILENO);
          fprintf(game->writeStream[position], "%s", "test message");
          fflush(game->writeStream[position]);
          close(parent2Child[1]);
    
          // Read from child -- not working
    
          /*dup2(child2Parent[0], STDIN_FILENO);
          close(child2Parent[0]);
          close(child2Parent[1]);
          */
    
      } else {
          // Setup child to read from stdin from parent
          dup2(parent2Child[0], STDIN_FILENO);
          close(parent2Child[1]);
    
          // Setup writing from child to parent
    
          /*
          if (dup2(child2Parent[1], STDOUT_FILENO) == -1) {
              fprintf(stderr, "dup2 in child failed\n");
          } else {
              fprintf(stderr, "dup2 in child successful\n");
              close(child2Parent[0]);
              close(child2Parent[1]);
          }
          */
    
    
          if ((int)execl("child", "2", "A", NULL) == -1) {
              printf("Failed child process\n");
          }
    
      }
     }
    

  • 我的 child 主要读有以下内容:
    char string[100];
    printf("reading from pipe: %s\n", fgets(string, 100, stdin));
    

    但是我不确定

    另外,不允许使用popen()或write()。显然也鼓励我使用文件流。

    最佳答案

    我主要谈的是您在父子进程之间建立双向通信的主要问题。如果您需要其他答案,请提出其他问题。

    您似乎有一个合理的通用方法,但是您确实存在一个严重的误解/设计缺陷:尽管有多个客户端将其标准流连接到用于与父进程进行通信的管道是合理的,但您不能连接父进程的所有端点如果您希望一次能够处理多个客户端,则将这些管道连接到父级的标准流。毕竟,父级只有一组标准流。为了支持多个客户端,父进程必须为每个客户端维护一对单独的文件描述符和/或流,并且必须通过它们而不是通过其标准流进行通信。

    我不确定当您挂起 child 对 parent 的方向时,为什么您的 parent / child 沟通会失败。该过程确实类似于设置其他端点。这是一个工作示例:

    parent.c:

    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <unistd.h>
    
    int main() {
      int child2Parent[2];
      int parent2Child[2];
      char buffer[256];
      FILE *p2cStream;
      FILE *c2pStream;
      pid_t pid;
    
      if (pipe(parent2Child) || pipe(child2Parent)) {
          perror("Failed to create pipes");
          exit(EXIT_FAILURE);
      }
    
      switch (pid = fork()) {
        case -1: /* error */
          perror("Failed to fork");
          break;
        case 0:  /* child */
          // Setup child to read from stdin from parent
          close(parent2Child[1]);  /* ignoring any error */
          close(child2Parent[0]);  /* ignoring any error */
          if ((dup2(parent2Child[0], STDIN_FILENO) < 0)
              || (dup2(child2Parent[1], STDOUT_FILENO) < 0)) {
            perror("Failed to duplicate file descriptors");
          } else {
            /* conventionally, the first program argument is the program name */
            /* also, execl() returns only if it fails */
            execl("child", "child", "2", "A", NULL);
            perror("Failed to exec child process");
          }
    
          exit(EXIT_FAILURE);
          break;
        default: /* parent */
          close(parent2Child[0]);  /* ignoring any error */
          close(child2Parent[1]);  /* ignoring any error */
          if (!(p2cStream = fdopen(parent2Child[1], "w"))
              || !(c2pStream = fdopen(child2Parent[0], "r"))) {
            perror("Failed to open streams");
            exit(EXIT_FAILURE);
          }
          if ((fprintf(p2cStream, "test message from parent\n") < 0)
              || fclose(p2cStream)) {
            perror("Failed to write to the child");
            exit(EXIT_FAILURE);
          }
          if (fscanf(c2pStream, "%255[^\n]", buffer) < 1) {
            perror("Failed to read the child's message");
            exit(EXIT_FAILURE);
          }
          printf("The child responds: '%s'\n", buffer); /* ignoring any error */
          break;
      }
    
      return EXIT_SUCCESS;
    }
    

    child.c:
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
    
    int main(int argc, char *argv[]) {
        char buffer[256] = { 0 };
    
        if (scanf("%255[^\n]", buffer) < 0) {
            perror("Failed to reading input");
            exit(EXIT_FAILURE);
        }
    
        /*
         * If stdout is connected to the parent then we must avoid
         * writing anything unexpected to it
         */
        if (fprintf(stderr, "received: '%s'\n", buffer) < 0) {
            perror("Failed to echo input");
            exit(EXIT_FAILURE);
        }
    
        printf("Hi, Mom!\n");  /* ignoring any error */
        fflush(stdout);        /* ignoring any error */
    
        return EXIT_SUCCESS;
    }
    

    与您的代码相反,请注意
  • 注意检查所有可能表明我关心的错误的返回值;
  • 父级使用标准流以外的其他流与 child 进行通信(尽管只有一个 child ,这是方便而非必要);
  • execl()参数的约定。

  • 还要注意,对于等待“出现”的东西,以这种方式设置的IPC流上的I/O操作将自动产生这种效果。但是,如何或应该如何利用它肯定是一个不同的问题。

    关于c - 了解管道,重定向和IPC,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/32823609/

    10-12 07:36
    查看更多