Re: Program hanging when read()ing from pipe



"Julien" <j_____@xxxxxxxxxxxxxxxx> wrote in message
news:43e8b734$0$592$636a15ce@xxxxxxxxxxxxxxx
My process forks. The child exec()s an external program. The parent
communicates with it through pipes connected to the child's
std{in,out,err}. At some point, the child (over which I have no control)
spawns another external process which goes in the background but
unfortunately inherits its parent's stdio. But my process does not need
to communicate with it and it is not supposed to output anything (appart
from some error messages that we be safely ignored). It should actually
stop the while(read()) loop (on the pipe connected to stdout, stderr is
checked later).

This could cause a deadlock where the child is blocked writing to stderr
(because it fills the pipe buffer) while your program is blocked reading the
child's stdout (after you've read everything written before the child
blocked). You need to use non-blocking I/O (in conjunction with
select()/poll()) to avoid this possibility.

Similarly, there is a potential deadlock where you block writing to the
child's stdin and the child blocks writing to stdout.

Since the pipe is inherited between the external programs, read() is
blocking. That's what I plan to solve with a SIGCHLD handler.

Does the external program write something to the pipe that you need to
read in its grandparent?

No.

If not, why does the external program need the write end of the pipe?

The child needs it to communicate but the grand child doesn't - but
inherits it after the child's fork().

Do you still see any issue with the parent closing the pipes upon
receiving SIGCHLD (after calling read() if there is something in the
pipe) ?

You previously wrote:
I actually solved the problem by catching SIGCHLD and using select() to
check if there is anything more to read in the buffer prior to closing
the read end of the pipe in the parent process. If the buffer is not
empty, then read() is called one more time.

There are two solvable issues I can see with this. The first is fairly
obvious: what if there are more bytes in the pipe than you read in the extra
call to read()? The second is rather theoretical: even if select() says the
pipe is readable, that doesn't mean a subsequent read() won't block.

Both of these issues are solved by the alternative I gave: after you get
SIGCHLD, make the pipe non-blocking and call read() in a loop until it fails
with EAGAIN.

In practice, I think this will read everything previously written to the
pipe (which, in your case, being done after getting SIGCHLD, must include
everything written by the child - if the child has exited it can't write any
more!). However, I am not convinced that this is guaranteed.

I have thought of another solution, although I don't know if it makes sense
or even is practical or possible in your case. If it does make sense, and is
both practical and possible, it is IMO preferable to the above (in part
because of the niggling uncertainty).

The idea is very simple: somehow (I can't suggest how) arrange for the child
to spawn a "stub" program instead of the external program. The stub closes
the pipe(s), open()s descriptors 0, 1, and 2 so they point at /dev/null,
then execs the real program (without forking).

With this in place, you should get EOF in your program when you were
originally expecting it. You won't need to handle SIGCHLD - just call wait()
after you get EOF.

From what you've said, it seems likely that I would consider one (or both)
of the external programs broken. Either the first should spawn the second
with stdin, out and err redirected to /dev/null, or the second should close
them when it starts.

Alex


.



Relevant Pages

  • Re: anonymous pipes
    ... After creating the child the first pipe ... BOOL fSuccess; ... GetCurrentProcess, &hChildStdoutRdDup, 0, ...
    (microsoft.public.vc.mfc)
  • Re: How does one effect O_NONBLOCK?!
    ... child and parent processes and then dup2the 3 descriptors into the ... corresponding ends of the pipes in the child before execve-ing. ... If I understand it correctly with "stdin stuff" you mean the write ... end side of the pipe that's connected to stdin of the child process. ...
    (comp.unix.programmer)
  • Re: How does one effect O_NONBLOCK?!
    ... once I've created 3 pipes (for stdout, stderr and stdin) ... child and parent processes and then dup2the 3 descriptors into the ... corresponding ends of the pipes in the child before execve-ing. ... end side of the pipe that's connected to stdin of the child process. ...
    (comp.unix.programmer)
  • Re: Problem with status value returned by waitpid
    ... > pages say that 'set the action for SIGCHLD to SIG_IGN is not allowed by ... system you aren't interested in the exit status of your child processes. ... write side of the pipe is still open in the parent. ...
    (comp.unix.programmer)
  • Re: Question about pipes
    ... >> (or even pseudo code). ... > It presupposes that on your system, file descriptor zero refers ... > is attached to the read end of the pipe. ... > the child may block if there is insufficient ...
    (comp.os.linux.misc)