Technical Linux questions
John Chambers
jc at trillian.mit.edu
Sat Feb 22 10:59:13 EST 2003
Nathan Meyers wrote:
| On Sat, Feb 22, 2003 at 04:49:42AM +0000, John Chambers wrote:
| > Which reminds me of a question I've never really seen
| > answered: How exactly do kernel threads differ from the
| > result of a vfork() call?
|
| In intent, if nothing else. Threads are intended to allow for multiple
| threads of execution in the same process space; vfork is intended to
| spawn a child whose only purpose is to immediately exec() a different
| executable. One description I found somewhere - I'm not sure how accurate
| - is that the parent blocks until the new child exec()s a new executable.
On the projects where I used vfork(), I never saw this. It
would have been a showstopper, of course, and reported as a
fatal bug. I do recall warnings in man pages about the
dangers of the child process changing memory before the
exec; this implies (but doesn't prove) that the parent is
running. But it's moot now that vfork is widely implemented
as fork, so it can't be used if you want portability.
| There's a lot of context management needed to make threads work
| properly. Given its limited functionality, vfork() doesn't have to
| provide such management - that's more than a documentation difference :-).
While this is true for a lot of programs, it isn't true for
all. And in any case, system calls are rarely actually
needed for much of this. The work can be done in user space
(though some sync primitives will require some judicious
use of machine language to get atomicity).
One example I saw but didn't implement myself: I worked for
a while (at Prime, R.I.P.) on a Sys/V unix system that had
hundreds of processors. There was a lot of software that
had been modified to take advantage of this. This included
a version of make that would run subprocesses in parallel
when they didn't have common dependencies. This was really
slick. On a box with about 200 385 processors, a system
build took about 2 minutes.
I talked to some of the implementers on one occasion, and
found out that modifying make this way wasn't all that
difficult. What they said they did was to take advantage of
vfork to get multi-tasking withing a single program. The
top-level make built the tree of dependencies, which was
then a nearly-static global structure. It then called vfork
to fire up 2nd-level makes on subtrees that were
independent. The only local storage these needed was the
command buffer and a few pointers, which could easily be
allocated on the stack. Each make could read the entire
dependency tree, but only modified its own subtree. A store
of an int or a pointer was atomic, so no synchronization
was necessary. The 2nd-level makes could also start their
own 3rd-level makes, and so on. Each make had to wait on
its own children, of course, which again made sync
functions other than wait() unnecessary. But note that
blocking the parent of a vfork() shoots down this approach,
since the parent needs to create multiple children before
waiting on all of them.
Granted, some multi-threaded programs need interaction
between threads, which implies tools to keep them from
shooting each other in the foot. But there are also
applications where none of this is needed. Some tasks can
be divided cleanly into subtasks, in which case a single
global memory area is the simple, elegant solution. Such
tasks can be handled simply and efficiently by the original
vfork(), if it just does the minimal work of creating a new
process with its own stack and the parent's data area, and
doesn't try to play any other tricky games.
More information about the Discuss
mailing list