How to implement two way communication between child and parent processes using pipes which involves multiple read and writes

Go To StackoverFlow.com

1

I have implement a scenario which involves two way communication between child and parent processes. The child process uses execvp to launch another c program (say XYZ). Parent writes data at one end of pipe1 and other end of pipe1 is duplicated to child's stdin. so child reads this data, performs some manipulations and writes data back to stdout. This stdout is duplicated to write end of pipe2 and now parent reads from read end of pipe2. The external program XYZ is not to be modified. The sequence to be followed is

1) parent writes data to pipe1 2) child reads data from duplicated end (stdin) of pipe1 3) child does some modifications to data and writes data back to stdout, which is duplicated to read end of pipe2 4) parent tries to read data from read end of pipe2.

The above scenario can be repeated n number of times.

Example of above scenario with example 1) parent writes 1 2) child reads 1 and does modification and writes 1 (after modifcations) to stdout 3) Now parent reads this data and prints back 4) after printing, parent writes 2 to the stdin 5) continues as above.....

The problem i am facing is parent is not able to read data from read end of pipe2. Its literally hanging out.

Imp Note: I have not closed the write handle of pipe1 because, i need to write data again. If i close the handle, i cannot reopen it, as it is not supported by pipes.

The code is as shown below

void Child_write  (pid_t Handle);
void Parent_write (pid_t Handle, char c);
void Child_read  (pid_t Handle);
void Parent_read (pid_t Handle);

void main()
{

  pid_t Pid;
  int   writepipe[2],readpipe [2];  

  pipe(readpipe);               /* Create two file descriptors  */
  pipe(writepipe);              /* Create two file descriptors  */

  Pid = fork();

  if (Pid == 0)         
  {
     close(writepipe[1]);               /* closing writepipe's write end */
     dup2(writepipe[0],0); close(writepipe[0]);     /* duplicating writepipe's read    end to stdin*/
     close(readpipe[0]);                /* closing readpipe's read end*/
     dup2(readpipe[1],1);  close(readpipe[1]);      /* duplicating readpipe's write end to stdout*/    
     Child_read(writepipe[0]);              /* reading data from write pipe read end and then duplicating*/

  }
  else                  
  {
     close(writepipe[0]);               /* closing writepipe's read end */
     Parent_write(writepipe[1],'1');            /* pupming data to the writepipe */
     close(readpipe[1]);                /* closing the readpipes write end */
     Parent_read(readpipe[0]);              /* reading the data which is pumped into readpipe */
     //Parent_write(writepipe[1],'2');          /* pupming data to the writepipe */
     //Parent_read(readpipe[0]);

     //Parent_write(writepipe[1],'3');
     //Parent_read(readpipe[0]);
     puts("***** End of parent process*****");
  }
}

void Child_read(pid_t handle)
{
   static char* command = "./stdoutput";
   execvp(command, NULL);
}

void Parent_write(pid_t handle, char c)
{
   char Buff[] = {'\n','\n'};
   Buff[0] = c;
   int n_written= write(handle, Buff, strlen(Buff)+1);

   printf("write function has written %d no of bytes and the data written is %s",n_written,Buff);

   //close(handle);
}

void Parent_read(pid_t handle)
{
    printf("PARENT PROCESS: In Parent_read function\n");
    int i=0;
    int bytes_read = 0;
    char buffer[1024]= {'\0'};
    while (read(handle, buffer, sizeof(buffer)) > 0)
    {
        printf("PARENT PROCESS:: In while loop\n");
        printf("The character/string read from readend of readpipe (stdout) is %s",buffer);
    }
    close(handle);
}

The external program is as shown below

void printing()
{
  int c;
  do
    {
        c = getchar();
        printf("%c",c);
    } while ((c != EOF) && (c != '\n'));
}

void main()
{
  printing();
  printing();
}

Please help me !!!!!!!!!

2012-04-04 04:26
by chaitu
Unless you're feeling masochistic, just use popen. Though at one time this only allowed unidirectional communication, Linux has supported bidirectional for quite a while, at least if memory serves - Jerry Coffin 2012-04-04 04:37
@JerryCoffin : i have no knowledge on popen(). Is it possible to solve the problem with unnamed pipes itself!! - chaitu 2012-04-04 05:27


1

On the first I/O call (using file stream functions) for a given FILE *, the library decides wether the stream should be line buffered, block buffered, or unbuffered. In your case, when stdoutput starts, it's stdoutput is not a terminal, so the stream goes in block buffered mode, and your printf ends in a buffer.

Change your main function to :

void main()
{
  setvbuf(stdout, NULL, _IONBF, 0);
  printing();
  printing();
}

And your printf call will produce writes.

You shoud also look at the setvbuf man page, and learn to use strace :

strace -ff -o test.log ./myprog

will produce two log fil (one for the parent, one for the child) that will let you see what system call are made by each program.

2012-04-06 08:35
by shodanex


-1

Indeed, as Jerry Coffin commented, you should use popen (some systems also have a p2open for bidirectional piping, one in, another out).

If you really insist on doing it by yourself, you should strongly consider using a multiplexing system call like poll (or perhaps the older select). Then you'll be able to test which file descriptor can be read or written, and act accordingly.

2012-04-04 05:29
by Basile Starynkevitch
if i create a file descriptor using popen() and closes it using pclose(), is there any way to re-open the same file descriptor back!! - chaitu 2012-04-04 06:16
popen don't directly create a file descriptor ; only the open, dup, pipe system calls do that. The popen library function, like fopen, gives you a FILE* handle, which is known to the C library (and contains an internal buffer, and an internal file descriptor). You can use fileno to get the file descriptor inside a FILE* handle. File descriptors are small non-negative integers (with 0 being stdin, 1 being stdout, ...). I don't understand why you want the same file descriptor (I believe you have a confusion) - Basile Starynkevitch 2012-04-04 06:22
in the scenario described in the post....i have to multiple reads and writes ( interleaved). so to make a (write,read) set successful, you need to close the write handle after successfully writing data. After this if i have to execute the same set again, i have to re open the closed file descriptor. This is the need for for me. If i dont close the write handle, the read will hang out - chaitu 2012-04-04 06:35
No, you need to use a multiplexing system call (e.g. poll) to choose if you want to read or to write. It is not a matter of closing a file descriptor (you'll do that when the child process exits). And you should not confuse FILE* handles (a pointer to an opaque data structure obtained with fopen(3) or popen(3) library functions and used e.g. in fscanf(3)or fprintf(3)library functions) and file descriptor (a small integer used in read(2) and write(2) system calls, obtained thru open(2), pipe(2) or dup(2) system calls). Don't confuse system calls with library functions - Basile Starynkevitch 2012-04-04 07:22
Ads