Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C: dup2, pipe and fork not working as expected

Tags:

c

fork

pipe

I'm trying to do a simple fork -> execute another program -> say "hello" to that child process -> read back something -> print what received.

The program used as child just waits for any line of input and prints something to the stdout like "hello there!"

This is my "host" program (that is not working):

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

#define IN 0
#define OUT 1
#define CHILD 0

main ()
{
  pid_t pid;
  int pipefd[2];
  FILE* output;
  char buf[256];

  pipe(pipefd);

  pid = fork();    
  if (pid == CHILD)
  {
    printf("child\n");
    dup2(pipefd[IN], IN);
    dup2(pipefd[OUT], OUT);
    execl("./test", "test", (char*) NULL);
  }
  else
  {
    sleep(1);
    printf("parent\n");
    write(pipefd[IN], "hello!", 10); // write message to the process    
    read(pipefd[OUT], buf, sizeof(buf));
    printf("received: %s\n", buf);
  }
}

I get this:

child
[.. waits 1 second ..]
parent
received: 

What am I missing? Thanks!

EDIT (test.c):

By request, this is the child program:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int getln(char line[])
{
  int nch = 0;
  int c;

  while((c = getchar()) != EOF)
    {
      if(c == '\n') break;
    line[nch] = c;
    nch++;
    }

 if(c == EOF && nch == 0) return EOF;
 return nch;
}

main()
{
  char line[20];

  getln(line);
  printf("hello there!", line);
  fflush(stdout);  
  return 0;
}
like image 929
pistacchio Avatar asked Oct 28 '11 21:10

pistacchio


People also ask

Why is dup2 not working?

It doesn't work because you're not using dup2() for its intended purpose. dup2() is for duplicating file descriptors, not for piping data from one stream to another. Look up the pipe() function if you want to redirect the output from one program to the input of another (but not the reverse).

What does the function dup2 1 0 do?

After dup2(f1, 0) , whatever file was opened on descriptor f1 is now also opened (with the same mode and position) on descriptor 0, i.e. on standard input. No locking is involved.

How to capture STDOUT in C?

To capture both stdout and stderr you can either create two separate pipes, or if it is acceptable for the streams to be mixed, copy the same file descriptor onto both STDOUT_FILENO and STDERR_FILENO by calling dup2 twice.

Why use pipe in C?

pipe() is used for passing information from one process to another. pipe() is unidirectional therefore, for two-way communication between processes, two pipes can be set up, one for each direction.


2 Answers

You're always suppose to read from file-descriptor 0, and write to file-descriptor 1 with pipes ... you have this relationship reversed in the parent process. For what you're wanting to-do, you may end up needing two pipes for two-way communication between the parent and child that avoids situations where the parent ends up reading the contents it wrote to the pipe since process scheduling is non-deterministic (i.e., the child is not guaranteed to read what the parent wrote to the pipe if the parent is also reading from the same pipe since the parent could just end up writing and then reading with no interleaving of the child process to read what the parent wrote).

Change your code to the following:

main ()
{
  pid_t pid;
  int pipe_to_child[2];
  int pipe_from_child[2];
  FILE* output;
  char buf[256];

  pipe(pipe_to_child);
  pipe(pipe_from_child);

  pid = fork();    
  if (pid == CHILD)
  {
    printf("child\n");

    //child process not using these ends of the pipe, so close them
    close(pipe_to_child[1]);
    close(pipe_from_child[0]);

    dup2(pipe_to_child[0], fileno(stdin));
    dup2(pipe_from_child[1], fileno(stdout));

    execl("./test", "test", (char*) NULL);
  }
  else
  {
    sleep(1);
    printf("parent\n");
    write(pipe_to_child[1], "hello!\n", 10); // write message to the process    
    read(pipe_from_child[0], buf, sizeof(buf));
    printf("received: %s\n", buf);
  }
}
like image 56
Jason Avatar answered Oct 13 '22 09:10

Jason


You need two pipes for this: one for the child process's stdin, and one for its stdout. You cannot reuse the two ends of a pipe as two pipes.

Also, this line of the parent program

write(pipefd[IN], "hello!", 10); // write message to the process    

does not write a newline, so getln in the child will never return. (Furthermore, "hello!" has only six characters, but you are writing ten.)

like image 37
zwol Avatar answered Oct 13 '22 09:10

zwol