了解管道,重定向和IPC

时间:2015-09-28 12:58:52

标签: c linux pipe

我是管道新手,并且一直在尝试创建一对管道,允许子进程写入父进程,以及父进程进行通信。有1名父母,最多4个孩子。孩子与exec成为一个不同的程序。

我的工作:

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

目标:

创建一个纸牌游戏,其中父母与每个客户进行对话(子进程),并向他们提供所有的移动和信息,从stdout到孩子的stdin。各个子进程在主要父级读取的stdout上回放他们的动作。游戏所做的动作完全取决于一个序列,而不是玩家。所以这是一个机器人游戏。

我坚持的内容:

我不知道如何获取它,所以父母可以通过文件流读取孩子的标准输出。当我尝试从子行设置读数时,代码似乎停止工作。即使是孩子也不能从父母那里读到(似乎停在现在注释掉的留置权,将孩子设置为父母)。

我也不确定在出现问题之前如何“等待”。比如,在开始时,玩家必须向父母发送“准备好”的消息,让他们知道他们正在工作。一旦我从孩子那里发送“准备好”,我如何无限期地“等待”直到下一条消息出现?

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

我收集的让父母给孩子写信的是:

  1. 首先创建管道
  2. 将父进程分解为另一个进程(子进程)
  3. 将管道连接到父级的标准输出,并使用dup2关闭父级的阅读侧并关闭
  4. 将管道连接到孩子的标准输入,并使用dup2关闭孩子的书写部分并关闭
  5. 从文件描述符中使用fdopen()获取文件流,然后打印到该文件流。
  6. 子进程stdin现在是从父进程打印到stdout的任何内容。
  7. 这是对的吗?我尝试将这种逻辑应用于孩子对父母的反对。

    1. 将输入管道连接到读取文件流,该文件流是子程序从其stdout写入的位置。
    2. 将输出管道连接到父流中读取的读取流。

      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");
            }
      
        }
       }
      
    3. 我的孩子主要有以下内容:

      char string[100];
      printf("reading from pipe: %s\n", fgets(string, 100, stdin));
      

      但我不确定如何

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

1 个答案:

答案 0 :(得分:1)

我主要谈到在父母和子女进程之间建立双向沟通的主要问题。如果您想要其他答案,请提出单独的问题。

您似乎有一个合理的通用方法,但您确实有一个严重的误解/设计缺陷:虽然多个客户端将每个标准流连接到管道以与父进程通信是合理的,但您无法连接父进程和#39;如果您希望一次能够处理多个客户端,那么所有这些管道都将转移到父级的标准流。毕竟,父母只有一套标准流。为了支持多个客户端,父进程必须为每个客户端维护一对单独的文件描述符和/或流,并且必须通过这些描述符和/或流进行通信而不是通过其标准流进行通信。

我不确定为什么你的父母/子女沟通失败时,你联系到孩子对父母的方向。该过程确实类似于设置另一个其他端点。这是一个有效的例子:

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;
}

与您的代码相反,请注意

  • 注意检查可能表示我关心的错误的所有返回值;
  • 父母使用除标准流之外的流与孩子沟通(虽然只有一个孩子,这是方便而非必需);
  • execl()参数的约定。

另请注意,至于等待某些内容&#34;出现&#34;,您以这种方式设置的IPC流上的I / O操作将自动产生该效果。但是,你如何能够或应该如何利用它肯定是一个不同的问题。