a pipe exercise

7 views
Skip to first unread message

Meredith Montgomery

unread,
Mar 2, 2022, 12:44:22 PMMar 2
to
I took Lew Pitcher's program and gave myself the exercise of just
switching parent and child's role. Briefly, Lew Pitcher's program forks
and the child writes data to the parent, which executes less to page the
output. What you find below is my own writing of that program (with the
roles switched) --- so all errors are mine, of course.

I think this program is wrong because the parent doesn't wait for the
child, so the child finishes ``quickly'' and must be left in a zombie
state until less exits. When less exits, it does so without calling
wait(), so the child ends up being wait()ed by the init program. Things
might end up well, but it's a totally flawed design.

That's my understanding. I appreciate if you correct any
misunderstandings here. Thank you.

(*) Full program

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
int pfd[2]; int r;

r = pipe(pfd); if (r < 0) { perror("pipe"); exit(1); }
r = fork(); if (r < 0) { perror("fork"); exit(2); }

if (r == 0) {
close(pfd[0]);
r = dup2(pfd[1], STDOUT_FILENO); if (r < 0) { perror("dup2 child"); exit(4); }
close(pfd[1]);
for (int c = 1; c <= 100; ++c)
printf("Line %d\n", c);
fclose(stdout);
exit(0);
}

close(pfd[1]);
r = dup2(pfd[0], STDIN_FILENO); if (r < 0) { perror("dup2 parent"); exit(3); }
close(pfd[0]);
execl("/usr/bin/less","less",NULL);
fprintf(stderr, "%s: execl() failed - %s\n", argv[0], strerror(errno));

// who's gonna wait() for the child?
exit(0);
}

Scott Lurndal

unread,
Mar 2, 2022, 1:29:54 PMMar 2
to
Meredith Montgomery <mmont...@levado.to> writes:
>I took Lew Pitcher's program and gave myself the exercise of just
>switching parent and child's role. Briefly, Lew Pitcher's program forks
>and the child writes data to the parent, which executes less to page the
>output. What you find below is my own writing of that program (with the
>roles switched) --- so all errors are mine, of course.
>

Keep in mind that there is no guarantee as to whether the child
process runs before the parent, or that the parent runs before the
child, or that they don't run _at the same time_ (e.g. on a multicore
processor). In this case, the child could have run to completion before
the parent returns from the fork() system call.

Lew Pitcher

unread,
Mar 2, 2022, 3:30:34 PMMar 2
to
If I understand correctly, /that/ situation should result in a pipe
that contains all the data from the child process, and (other than the
write fd in the parent process) no open write fds.

So long as the parent process closes that lone pipe write fd (which
it does, in Meredith's code) the reader should properly receive EOF
once it has read the entire contents of the (buffered) pipe.

--
Lew Pitcher
"In Skills, We Trust"

Lew Pitcher

unread,
Mar 2, 2022, 3:43:57 PMMar 2
to
On Wed, 02 Mar 2022 14:44:11 -0300, Meredith Montgomery wrote:

> I took Lew Pitcher's program and gave myself the exercise of just
> switching parent and child's role. Briefly, Lew Pitcher's program forks
> and the child writes data to the parent, which executes less to page the
> output. What you find below is my own writing of that program (with the
> roles switched) --- so all errors are mine, of course.

It looks like a reasonable test program to me. I walked through it and
didn't see any obvious problems.

> I think this program is wrong because the parent doesn't wait for the
> child, so the child finishes ``quickly'' and must be left in a zombie
> state until less exits. When less exits, it does so without calling
> wait(), so the child ends up being wait()ed by the init program. Things
> might end up well, but it's a totally flawed design.

Perhaps not a CYA thorough design, but I wouldn't say "totally flawed".
It /would/ be better to have the parent wait() for the child process, but
since it doesn't, that child process (if it hasn't terminated) becomes an
orphan process, not a zombie. (It would only be a zombie during the lifetime
of the parent process.)

As you alluded to, init(8) is specifically tasked with cleaning up the
status of orphaned child processes. So long as the parent process exits
in a timely manner (and yours does, more or less) there's no need to worry.
Or, to put it another way, so long as the zombie doesn't live /too/ long,
and there aren't /too/ many zombies, the system will live on.

Remember, zombie processes starve the system by holding on to a limited
resource: their place in the system's process state table. The more zombies,
the fewer live processes can run. But, zombie processes are tolerable, so
long as they don't live long enough to impact performance. They may be
undesirable, and (consequently) a sign of "bad design", but so long as they
/do/ die in a reasonable time, you can get away with them.

> That's my understanding. I appreciate if you correct any
> misunderstandings here. Thank you.

[snip code]
Reply all
Reply to author
Forward
0 new messages