Interactive two-way communication (using pipes) between child and parent processes

I am trying to create a child process, redirect its stdin and stdout to the parent process, and communicate with the child process. That is, the parent process must be able to receive input from the child, process it, and provide output to the child, and the loop repeats (for example, user interaction with the shell, which is actually the end goal: simulate user interaction).

So far, I've been able to successfully create and redirect pipes (the code below just echoes the parent's input, from the user, through the child). The problem is I can only do this once, when the child ends after the first I / O cycle and I get SIGPIPE. How can I keep the child alive and read it from the feed when the parent writes to it?

PS I know, besides that, that can be used to simulate user interaction, but I need access to the returned data to do custom analysis, and I would like to go that way for academic purposes.

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

      

+3


source to share


2 answers


Your child process exits after the following reading:

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

      



This, of course, causes the pipes to close and your parent gets a SIGPIPE while trying to write to a pipe that no one else is reading from. Your parent code uses a loop to read / write to / from the child - do something similar in your child code - run in a loop, not just exit. You can also use a special character string that will cause your child to exit the loop and exit.

Also, if you want to read / write with stdin and stdout, you can call exec () after duplicating the descriptors with dup2

and close the correct pipe ends. In your child process, you can use regular C ++ streams like std :: cin and std :: cout. This, of course, requires refactoring your child code into a separate program.

+2


source


The poll and select families keep track of file descriptors and wait until they are ready for I / O operations.



I ended up using pseudo-terminals for two-way communication, but they work with pipes as well.

0


source







All Articles