[OT] poll() vs. AIO (was: [PATCH] ash: clear NONBLOCK flag from stdin when in foreground)

Rich Felker dalias at aerifal.cx
Mon Aug 22 01:34:20 UTC 2011


On Mon, Aug 22, 2011 at 03:18:11AM +0200, Laurent Bercot wrote:
> > I'd like to know how else, other than this ugly SIGCHLD handler, you
> > can create a child process without having the keep track of the pid
> > and have part of your code be responsible for waiting for it.
> 
>  But it *is* necessary to keep track of the pid! fork() returns a pid

In a way you're right. Since child processes can get killed
independently, it's almost always a mistake to "detach" or "disown"
them and rely on them completing their task. But there are alternate
ways to do this like using the communication channel (pipe/etc.)
rather than the pid.

> for a reason; good programming practice is for the SIGCHLD handler to
> wait (with W_NOHANG) for zombies, call a possible termination routine
> if the pid is in the list of known children pids, and *do absolutely
> nothing* if it is not.

This requires a global list/global state that is inherently
incompatible with library use, and just fundamentally bad programming.

>  Or maybe your point was that the main SIGCHLD handler has to wait for
> all children anyway, so it is impossible for library code to do the
> waitpid() for forked processes? Well, yes, that is right, library code
> cannot rely on a waitpid() to detect child termination. It has to use
> another mechanism and let the reaping be handled by the main SIGCHLD
> handler.
>  Fortunately, there is a trivial mechanism to detect child termination:
> pipes reading from the child read EOF when the child is dead. That is
> how library code can forkexec helper programs without any trouble.

Unfortunately pclose and system use waitpid, and it's rather common
practice. If your library code is not going to use waitpid, you need
to document that the caller is responsible for handling SIGCHLD and
reaping zombies.

> > I've timed it. fork() takes as least twice as long as pthread_create
> > and that's including the time pthread_create spends on mmap, mprotect,
> > etc.
> 
>  That's still a puny price to pay for the protection that separate
> processes offer.

I agree it's fairly low in most actual usage cases.

> > I already explained how this could lead to *unbounded* memory growth
> > through leaks that are nearly impossible to fix, unless all your
> > forking happens from a common parent that doesn't grow.
> 
>  You keep opposing badly written multiprocess code to well-written
> multithreaded code.

I'm not sure how what I was imagining would qualify as "badly
written". In my view, a program that had lots of extra logic to keep
track of what data would no longer be needed in the child after
forking and free it all would not qualify as "well-written" but as a
maintenance nightmare.

> Let's not go down that road, because I have seen
> the horrors that come from badly written multithreaded code and there is
> no way you can deny it's the worst nightmare ever.

I'd have a hard time choosing a single "worst nightmare ever" when it
comes to code, but otherwise I agree completely.

>  Correctly written multiprocess code forkexecs from a single parent
> unless there's a very good reason to do otherwise, and does not arbitrarily
> fragment memory.

I'm thinking of an application model where the main event loop creates
a new thread/process for handling events for which it cannot complete
the processing immediately, and the possibility that those handlers,
in turn, might have subtasks which themselves cannot be completed
immediately. With threads this requires almost no code, unless you're
doing things horribly wrong (which I admit many people do). With
forking, it requires painful synchronization back with the main
process's event loop, and artificially inflates the levels of
interdependence between modules.

Rich


More information about the busybox mailing list