Hopefully a simple question. I’m trying to learn, simultaneously, fork(), pipe(), and waitpid() and running into some problems.
if (pipe(myout)<0 || pipe(myin)<0 || pipe(myerr)<0) { perror("Couldn't make pipes"); return; }
int childpid=fork();
if (childpid==0) { //child
fdopen(myout[1], "w");
fdopen(myin[1], "r");
fdopen(myerr[1], "w");
dup2(myout[1], 1);
dup2(myin[1], 0);
dup2(myerr[1], 2);
printf("This should be seen\n");
fclose(stdout); fclose(stdin); fclose(stderr);
sleep(10);
_exit(0);
} else { //parent, wait on child
printf("parent, monitoring\n");
sim_out=fdopen(myout[0], "r");
sim_in=fdopen(myin[0], "w");
sim_err=fdopen(myerr[0], "r");
printf("have my fds\n");
int status;
do {
int ch;
if (read(myout[0], &ch, 1)>0)
write(1, &ch, 1);
else printf("no go\n");
waitpid(childpid, &status, WNOHANG);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
I’m getting:
parent, monitoring
have my fds
T
before the program exits – that is, the loop runs only once. I have a check below that, and it’s coming up WIFEXITED() so the process is supposed to have exited normally. But what bothers me is that there’s a sleep(10) before that happens, and this happens immediately – not to mention that the child processes are left running for the remaining wait time.
I’m fundamentally misunderstanding something, clearly. My expectation was that the parent loop would block until it sees a character from the child, read it, then check to see if it was still alive. I certainly didn’t expect waitpid() to set WIFEXITED when the child was still alive.
Where am I wrong?
I think I can see several issues. I’ll try to mention them in order of appearance.
forkfor the return value-1which indicates an error (no child will have been forked in this case).fdopenleak resources (depending on the implementation; the one on RHEL4 leaks). They return aFILE*which you could then use infwriteetc. and then close by doingfcloseon them. But you throw that value away. You don’t need to open the pipe for reading/writing. Pipes are suitable for that when created.close (myin [1]); myin [1] = -1; close (myout [0]); myout [0] = -1; close (myerr [0]); myerr [0] = -1;dup2are technically fine on all Linux variants I know, but it is customary to use the first fd of a pipe for reading and the other for writing. Thus, yourdup2s would be best changed todup2 (myin [0], STDIN_FILENO); dup2 (myout [1], STDOUT_FILENO); dup2 (myerr [1], STDERR_FILENO);close (myin [0]); myin [0] = -1; close (myout [1]); myout [1] = -1; close (myerr [1]); myerr [1] = -1;statusfor your child’s exit code. Butwaitpidhas not yet been called. You should checkwaitpid‘s exit code and not evaluatestatusif it returns something other thanchildpid.Edit
Since only the pipe ends that you actually need are open now, the operating system will detect a broken pipe for you. The pipe breaks when the child does
fclose (stdout). The parent can still proceed to read all the data that may be in the pipe, but after thatreadwill return zero, indicating a broken pipe.Thus, you can actually spare the call to
waitpid. You could instead simply wait forreadto return zero. This is not 100% equivalent, though, since your child closes its end of the pipe before it goes tosleep(causing the parent to proceed when all data have been read), whereas thewaitpidversion, of course, only proceeds when the child actually died.