Newsgroups: comp.os.minix
Subject: Re: Minix Project
References: <6s1z21natq.fsf@iago.nac.net> <8i8qb9$2i9$1@fangorn.moria.de> <39490f4e@news.isc.rit.edu> <xlfsnuej0wb.fsf@wintermute.informatik.uni-tuebingen.de>
Organization: Rochester Institute of Technology, Rochester, NY
From: aje9383@osfmail.isc.rit.edu (Andrew Erickson)
NNTP-Posting-Host: grace.isc.rit.edu
X-Original-NNTP-Posting-Host: grace.isc.rit.edu
Message-ID: <3949b86e@news.isc.rit.edu>
Date: 16 Jun 2000 01:17:34 -0500
X-Trace: 16 Jun 2000 01:17:34 -0500, grace.isc.rit.edu
Lines: 150
XPident: aje9383
X-Original-NNTP-Posting-Host: 129.21.4.100
XPident: Unknown
Path: news.adfa.oz.au!clarion.carno.net.au!news0.optus.net.au!news1.optus.net.au!optus!intgwpad.nntp.telstra.net!news-spur1.maxwell.syr.edu!news.maxwell.syr.edu!newsswitch.lcs.mit.edu!bloom-beacon.mit.edu!news.kodak.com!news-nysernet-16.sprintlink.net!news.sprintlink.net!news.isc.rit.edu!aje9383
Xref: news.adfa.oz.au comp.os.minix:35426

In article <xlfsnuej0wb.fsf@wintermute.informatik.uni-tuebingen.de>,
Markus Leypold  <leypold@informatik.uni-tuebingen.de> wrote:
>aje9383@osfmail.isc.rit.edu (Andrew Erickson) writes:
>> Ideally, we should eliminate fork() altogether; it is one of the biggest
>> design flaws in UNIX.  "Let's precisely copy this process now so we can,
>> after five more instructions, replace the image with a new one.  Maybe, if
>> we sell it properly, people will one day even claim this is efficient."
>> It's an inherently wasteful model; even with COW et al. a lot of unnecessary
>> bookkeeping happens in the most common cases.  There are, indeed, some cases
>> where fork() is useful; but the vast majority of these would be better
>> served by fork()ing threads rather than processes.  For process creation, a
>> spawn() call is far more efficient.
>
>Both cases: no. Andrew, I do not agree with that. In my eyes it is
>actually a very ingenious trick to let the parent process (or better:
>it's child after fork, but still the parent program) do the setup of
>the new prozess-to-be. Threads have a number of disadvantages: (1) No
>separate adressspace (2) Hence difficult to program.  (3)
>Multithreaded Servers accumulate errors (as multithreaded HTTP-Servers
>do), since they never get restarted. Most Servers using multiple
>copies of themselves, created with fork(), serve a finite number of
>requests, then are replaced by a fresh copy. All resources still hold
>erronously are freed then.

From a purely pragmatic point of view, having separate address spaces does
indeed make programming a bit easier and systems somewhat more robust.  I'm
not quite sure I entirely agree with you that the operating system should
make it easier to get improperly written (buggy) servers working reliably;
on one hand, a server (especially) should not leak memory, etc; on the other
hand, real code is often not that great, and having imperfect code work is a
nice thing from the user's prespective.

I probably should have quickly explained what I was meaning by spawn().  On
operating systems which use such a model, spawn() (or whatever the designers
decided to name the system call) essentially does both a fork() and an
exec().  An executable is passed the spawn(), and a new process running the
program is created and started.  Obviously, some variations exist in how to
specify the executable, etc. etc.; but that's the general idea.

>Making fork() efficient is up to the OS-Designer.

An OS designer whoose only concern was efficiency in launching programs
would not make fork() efficient, but would use something more like spawn(). 
A sane OS designer is concerned with some other things, as well, such as the
ease of running servers.  As with most other decisions, there are tradeoffs
all over the place.  I don't think the ones involved with selecting fork()
are the best for general-purpose uses--but that's as much prejudice on my
part as it is hard facts.  (I haven't done any real testing, for instance,
to see if there is any notable difference in speed.)

>> "But," the skeptic asks, "how would you be able to set up standard input and
>> other file descriptors with spawn?"  One solution is to allow processes to
>> be spawn()ed in a dormant state, and have a few system calls for
>
>Well, this syscall exists. It's called vfork().

vfork() is not really what I have in mind.  vfork() is a performance hack
which makes up for much of the inefficiency of fork() by imposing very
strict constraints on what the processes are allowed to do.

>> manipulating the file descriptors on a dormant, newly-spawn()ed process. 
>
>Well, this has to be done by somebody. Doing it with the child before
>exec() saves a lot of new system calls.

That's true, and a good point.

>> Once things are set up properly, it could start running.
>> 
>> I've always been a bit incredulous at UNIX fanatics who vociferously claim
>> how effecient and lightweight process creation is in UNIX with fork().  It
>
>On systems with good virtual memory, fork() is very efficient. One has
>copy-on-write for the writeable data pages, so fork() only has to make
>some entries in the process list and set up a new virtual memory page
>table. If a exec() happens immediately afterwards, nothing has really
>happened.

As I understand things, these are the possibilities when starting up a new
process (COW = copy on write):

fork()/no COW (e.g. Minix):
  Parent fork()s:
    Process image is copied
    New process started
  Child exec()s:
    Child process image is overlaid (destroyed and recreated)

fork()/COW (e.g. most modern UNIX implementations)
  Parent fork()s:
    Parent's data pages are marked read only
    Parent's pages are marked shared, etc.
    Child's page table set up
    New process started
  [a couple of pages are copied on write before...]
  Child exec()s:
    Shared pages are unmarked shared, etc.
    Child's page table destroyed, set up again

vfork() (e.g. BSD system with BSD software)
  Parent vfork()s:
    Child proceses is created, uses same page table as parent
    Parent is blocked
  Child exec()s:
    Child's page table is set up with new program
    Parent is unblocked, return status from vfork put on stack

spawn() (e.g. many non-UNIX based operating systems)
  Parent spawn()s:
    Child process is created
    Child's page table set up from new executable file

fork() with copy on write is much more efficient than fork() without copy on
write--but there is still a fair bit of page table meddling which is
performed and later discarded.  vfork() avoids this, but has messy
semantics, such as Thou Shalt Not Change Variables and Thou Shalt Not Return
From The Calling Procedure Of The Vfork.  (These stem directly from the fact
that the memory is physically shared between the processes.  POSIX allows a
vfork() to be identical to a fork(); thus changing variables in the child
may or may not change them in the parent; hence the first admonition. 
Likewise, breaking the second rule may have disasterous effects or no ill
effect at all, depending on the system.  I believe the only things
guaranteed by vfork() is that an immediately following exec() or exit() will
operate as expected.)

(I should probably point out that, on systems with no memory management
hardware, fork() is *extremely* expensive, as it can really only be done by
shadowing--that is, copying process images around on every context switch. 
The M68K is one such processor; and I happen to be stuck running Minix on
one.  It's painful watching MacMinix do things which fork() a lot, such as
some sorts of shell scripts.  There's a quite noticible pause between lines
which are echo'd to print them, for example.  It doesn't help that the
executables are generally not split instruction and data, either--that means
the code segment is copied back and forth with the data segment.)

That leaves spawn() as the best choice for starting up new programs.  Since,
on most interactive systems, processes are started up to run some new
program, I think it is the best process creation primitive overall.  The
problem I see is not that fork() is bad in itself, but that it maps poorly
to what is required most of the time; it is relatively rare that a user
wants a bunch of identical processes.  Something reasonably similar may be
achieved by spawn()ing multiple copies of the same program (or perhaps
another copy of oneself), but it's obviously not the same.

At any rate, these are academic discussions; since Minix is a clone of UNIX
at the system call level, it kind of has to use a fork() of some sort for
process creation.

-- 
Andrew Erickson
