子进程和父进程之间的交互式双向通信(使用管道)

时间:2015-07-08 22:00:36

标签: c++ linux pipe

我正在尝试创建子进程,将其stdin和stdout重定向到父进程,并与子进程交互。也就是说,父进程应该能够接收来自子进程的输入,处理它并向子进程提供输出,并且循环重复(如用户与shell的交互,这实际上是最终目标:模拟用户交互)。

到目前为止,我已经能够成功创建和重定向管道(下面的代码简单地回应父进程的输入,从用户到子进程)。问题是我只能这样做一次,孩子在第一次I / O循环后终止,我得到一个SIGPIPE。如何让孩子保持活着,并在父母写信给它时从管道中读取它?

P.S。我知道除了可以用来模拟用户交互,但是我需要访问返回的数据来进行自定义分析,我想这样做是出于学术目的。

#include <iostream>
#include <string>
#include <sstream>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <limits.h>
#include <sys/types.h>
#include <cstdlib>


#define MAX_CMD_SIZE 256
#define MAX_BUFF_SIZE 512

#define NUM_PIPES 2
#define PARENT_READ_PIPE pipes[0]
#define PARENT_WRITE_PIPE pipes[1]

#define READ_END 0
#define WRITE_END 1

#define PARENT_READ_FD PARENT_READ_PIPE[READ_END]
#define PARENT_WRITE_FD PARENT_WRITE_PIPE[WRITE_END]

#define CHILD_READ_FD PARENT_WRITE_PIPE[READ_END]
#define CHILD_WRITE_FD PARENT_READ_PIPE[WRITE_END]

int pipes[NUM_PIPES][2];


int main()
{
    pipe(PARENT_READ_PIPE);
    pipe(PARENT_WRITE_PIPE);

    pid_t process_id = fork();

    if (process_id<0) //throw diag::DiagError("ERROR: error during fork()");
    {std::cerr<<"ERROR: error during fork()"; exit(1);}
    else if (process_id==0)
    {//CHILD process
        dup2(CHILD_READ_FD, STDIN_FILENO);
        dup2(CHILD_WRITE_FD, STDOUT_FILENO);

        close(CHILD_READ_FD);
        close(CHILD_WRITE_FD);
        close(PARENT_READ_FD);
        close(PARENT_WRITE_FD);

        char buffer[MAX_BUFF_SIZE];
        int count = read(STDIN_FILENO, buffer, MAX_BUFF_SIZE-1);
        if (count >= 0) 
        {
            buffer[count] = 0;
            printf("%s", buffer);
        } 
        else //{throw diag::DiagError("IO Error");}
        {std::cerr<<"ERROR: error during fork()"; exit(1);}
        exit(0);
    }
    else
    {//PARENT process
       close(CHILD_READ_FD);
        close(CHILD_WRITE_FD);

        char buffer[MAX_BUFF_SIZE];

        std::string temp="";

        while(std::getline(std::cin,temp))
        {
            write(PARENT_WRITE_FD, temp.c_str(), temp.size());

            //wait(&status);
            int count = read(PARENT_READ_FD, buffer, MAX_BUFF_SIZE-1);
            if (count >= 0) 
            {
                buffer[count] = 0;
                printf("%s", buffer);
            } 
            else //{throw diag::DiagError("IO Error");}
            {std::cerr<<"ERROR: error during fork()"; exit(1);}
        }  
    }
}

2 个答案:

答案 0 :(得分:2)

您的子进程在一次阅读后退出:

    if (process_id<0) throw diag::DiagError("ERROR: error during fork()");
    else if (process_id==0)
    {//CHILD process
            .....
            exit(0); /* <-- here */
    }

这当然会导致管道关闭,而您的父管在尝试写入无人读取的管道时会收到SIGPIPE。 您的父代码使用循环来读取/写入子代 - 在您的子代码中执行simmilar事务 - 在循环中运行而不是仅退出。 您还可以使用一些特殊的字符串,这些字符会导致子进程退出循环并终止。

此外 - 如果你想使用stdin和stdout进行读/写 - 你可以在用dup2复制描述符之后调用exec()并关闭正确的管道末尾。在您的子进程中,您可以使用正常的c ++流,如std :: cin和std :: cout。当然,这需要将您的子代码重构为单独的程序。

答案 1 :(得分:0)

https://developer.mozilla.org/en-US/docs/Web/CSS/cursor1个系列监视文件描述符,并等待它们为I / O操作做好准备。

我最终使用poll进行双向通信,但它们也适用于管道。