2015-09-28 95 views
2

我是新来的管道,并试图创建一对管道,允许子进程写入父进程,并与父进程进行通信。有一位父母最多可以有4个孩子。这个孩子与执行官成为一个不同的计划。了解管道,重定向和IPC

我有什么工作:

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

目的:

要创建一个纸牌游戏,以每个客户的父会谈(子进程),并给出了所有的动作和信息给他们,从它的标准输出到孩子们的标准输入。个别的孩子进程退回他们的标准输出,由主要父母阅读。游戏的动作完全由一个序列决定,而不是玩家。所以这是一个机器人游戏。

我坚持什么:

我不知道如何得到它,家长可以通过文件流中读取孩子的标准输出。当我尝试从子行设置读取时,代码似乎停止工作。甚至连孩子都不能从父母那里读到(似乎停止在现在注释掉的留给父母的留置权)。

我也不确定如何“等待”,直到出现某种情况。就像,在开始时,玩家必须发回“准备好”的消息给父母,让他们知道他们正在工作。一旦我发送孩子的“准备好”,我如何无限期地“等待”,直到下一条消息出现?

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

我收集什么让家长写来的孩子是:

  1. 创建管道第一
  2. 叉掉父进程到另一个进程(子)
  3. 管道在连接到父stdout和使用DUP2,靠近
  4. 连接管的出孩子的标准输入封闭读出侧父,并用DUP2封闭写作部分的儿童和密切
  5. 使用文件描述符中的fdopen()获取文件流,然后打印到该文件流。
  6. 子进程stdin现在是无论您打印到父级的标准输出。

这是正确的吗?我尝试将这种逻辑应用于孩子到父母,但将其颠倒过来。

  1. 将管道连接到读取文件流,该文件流是子程序从标准输出写入的地方。
  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"); 
         } 
    
        } 
    } 
    

我的孩子主要具有读取它的情况如下:

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

但我不知道如何

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

回答

1

我主要谈到在父母和子女过程之间建立双向沟通的主要问题。如果您想要额外的答案,请提出不同的问题。

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

我不确定为什么你的父母/孩子的沟通失败,当你勾住孩子对父母的方向。该过程确实类似于设置另一个其他端点。这是一个工作示例:


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()参数。

还要注意,至于等待某些东西“出现”,IPC流上的I/O操作会自动产生这种效果。然而,你如何能够或应该如何使用这个问题当然是一个不同的问题。

+0

嗨,感谢您的帮助。所以我对你的代码做了一些修改。孩子:http://pastebin.com/6iq2n1JU,家长:http://pastebin.com/UPbbG6qg。但我得到的输出是:http://pastebin.com/g50gAeW1 这是基本阅读父母和孩子之间的正确方法吗?就像父母发送“Hi”一样,等孩子说出“Hi Mom”,然后说“你好吗?”回到孩子身边,等待回复? – user3139573

+0

如果您对我提供的代码的工作原理有何疑问,或者为什么它会按照原样编写,请随时询问。 –

+0

我在写完之前不小心发表了评论,对不起。每次写完后你是否应该关闭文件流(p2cStream)?我可以把它变成一个函数,比如write_to_child(char * message)那两行吗? – user3139573