C: dup2, pipe and fork not working as expected

2019-04-27 03:17发布

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

标签: c fork pipe
6条回答
Evening l夕情丶
2楼-- · 2019-04-27 03:40

It looks like you have your pipe descriptors mixed up. After calling pipe(), pipefd[0] is the read end of the pipe, and pipefd[1] is the write end of the pipe. You're writing to the read end, and reading from the write end.

Also, you're trying to use one pipe for both stdin and stdout of the child process. I don't think this is really what you want to do (you will need two pipes).

查看更多
Melony?
3楼-- · 2019-04-27 03:43

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);
  }
}
查看更多
疯言疯语
4楼-- · 2019-04-27 03:43

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.)

查看更多
在下西门庆
5楼-- · 2019-04-27 03:49

You probably should use wait or waitpid.

查看更多
beautiful°
6楼-- · 2019-04-27 03:59

Looks like you have your IN/OUT backwards for the pipe -- pipefd[0] is the read end of the pipe, so writing to it (as the parent does) is nonsensical and will fail. Similarly pipefd[1] is the write end so reading from it (as the parent does) will also fail. You should ALWAYS check the return values of the read and write calls, to see if you're getting any errors

查看更多
Ridiculous、
7楼-- · 2019-04-27 04:02

Others are saying that the pipe is mono-directional, which is what I thought at first. But actually that's not what my man page says:

 A read from fildes[0] accesses the data written to fildes[1]
 on  a  first-in-first-out  (FIFO)  basis  and  a  read  from
 fildes[1] accesses the data written to fildes[0] also  on  a
 FIFO basis.

However, this does mean that if the parent is writing to pipefd[0], then the child should read from pipefd[1], so you are associating the wrong side of the pipe with the child's stdin and stdout.

From the man page, it does seem like you can do this with one pipe. But it might be clearer code to use two.

It seems like you are thinking of each element of pipefd as a separate pipe, but that's not the case.

查看更多
登录 后发表回答