tags:

views:

74

answers:

2

i have a very simple c/c++ program that forks a child process to execute another program, and then sends some data to that child program, and waits for the response.

the child program reads from stdin and waits for EOF before it continues.

my problem is, the child program receives the initial input from the pipe writing, but it never sees an EOF (even though I close the pipe), so it waits forever.

i'm not sure why closing the pipe doesn't imply the EOF for the child's stdin?

here's the code:

http://gist.github.com/621210

+2  A: 

Updated with what I think is the problem: You are reading into a character and checking that character for EOF. That is not how the read() system call works. It will return a 0 when at EOF. It does not write an EOF into the buffer.

Also I see you are reading one character at a time. This is an awful way to read data. It is several thousand times slower than reading a large buffer, say 4 or 8 kB.

I believe you also have a common mistake here. You are not checking the return value of write().

The write system call is not guaranteed to write all the data before returning. It might write 4000 bytes and return. It will return the number of bytes written. It is then your responsibility to update your buffer pointer and call write again.

Or it might return an error code and it is important for you to check for that.

Zan Lynx
fair enough. this is simple proof-of-concept code. in my test, i'm sending 5 characters as the input. So I guess I'd be surprised if the write() was only sending half of them.
Kyle Simpson
moreover, if i change the child to only read exactly 5 characters (instead of waiting for EOF), the child program gets all 5 characters just fine and works as expected. so it seems the only hold up is that i need the child to wait for variable length input and EOF seems like the correct way to signal such.
Kyle Simpson
you're focusing on the wrong part of the problem. i have no issues getting the ouptut back from the child into the parent. i'm having issues with the child not ever getting an EOF when the parent writes some data to the pipe which is hooked up to the child's stdin and then closes the pipe after writing.
Kyle Simpson
see L44 and L46... L44 writes the data to the pipe, which should send to the child's stdin. L44 works fine. L46 closes the pipe after the write() is done. From what I knew before, closing the pipe should send the EOF down the pipe to the child's stdin. But it does not.
Kyle Simpson
@Kyle: I updated my answer. The first part now addesses your read() call which I think is wrong.
Zan Lynx
now that the other answer helped me fix the bi-directional i/o, i'm going to fix both the write() and the read() so that they are buffered and thus more robust. thanks for the tips.
Kyle Simpson
+1  A: 

The most common reason for this is that you aren't closing the write end of the pipe, so the EOF never gets sent. The common example is when you have code that looks like:

int fds[2];
pipe(fds);  // open a pipe
if (fork()) {
    // parent process
    write(fds[1], ...  // write data
    close(fds[1]); // close it
} else {
    // child process
    while (read(fds[0], ....) > 0) {
        // read until EOF

The problem here is that the write end of the pipe never gets closed -- the parent process closes it, but the child still has the write descriptor open. So the child never sees an EOF on the read descriptor.

The very first thing you need to do after forking the child is to close(fds[1]);, closing its copy of the write descriptor. That way when the parent closes the last remaining reference to the write end of the pipe, the child will see EOF on the read end.

Edit

looking at the link you added, this is precisely the problem -- the child still has the write end of the pipe open on its stdout. Don't dup the write end to stdout in the child, just close it. Send stdout somewhere else (a logfile, or /dev/null)

Edit

for bi-drectional communication, you'll need two pipes:

int tochild[2], fromchild[2];
pipe(tochild); pipe(fromchild);
if (fork()) {
    close(tochild[0]);
    close(fromchild[1]);
    //write to tochild[1] and read from fromchild[0]
} else {
    dup2(tochild[0], 0);
    dup2(fromchild[1], 1);
    close(tochild[0]); close(tochild[1]);
    close(fromchild[0]); close(fromchild[1]);
    exec(...
}

You need to be very careful writing data in the parent, however -- if there's a lot of data to be sent to the child, you can't send all of it before reading the child's output or you might deadlock (both pipes fill up and the parent blocks trying to write more data for the child while the child blocks trying to output). You need to use poll or select to tell when there's data to read or space to write, and you may want to put the pipes (the parent ends at least) in non-blocking mode.

Chris Dodd
please see lines 32-37 in the linked code gist. i am closing them.
Kyle Simpson
line 33 dups it to stdout, which is then never closed
Chris Dodd
how can actual `stdout` of the child be closed if i need the child to be able to respond back to the parent after it's done!? i need a two-way communication channel here. i need to send data to the child, and i need to see what the child responds with.
Kyle Simpson
and btw, i do not have general control of the child executable. it's just a program that reads input, does something, and responds with output.
Kyle Simpson
do i need two pipes? one for the communication from parent to child (the write) and one for child to parent (the read)? seems from initial testing that may be so.
Kyle Simpson
a pipe is a one-way communications channel - the way you have it now, the output from the child will be fed back to the input of the child. If you want two-way communication, you'll need two pipes
Chris Dodd
@Chris-ok, so regarding your final note there about needing to be careful about deadlocking: so, the child doesn't output anything at all until it's finished reading everything in from the parent. in other words, there's only ever writing or reading going on in the pipes at one time, not both. am i still in danger of not being able to have the child read in as much input as the parent wants to send to it? and vice versa, since all the input will be done before the child starts outputting, will i have a limit for how much the child can output?
Kyle Simpson
AS long as the child reads all its input before writing anything, you should be fine. Problems can occur if the child starts writing before it has completely read its input.
Chris Dodd