Why are threads and processes different? Here are some
reasons, both subjective and objective.
By their definition, threads do not have to be visible
outside of a process. This allows (but does not require)
thread creation and scheduling to be largely performed at
user level. Some advantages of this are:
Fewer kernel context swaps in most cases. 250
instructions for a null system call can swamp the cost
of a fast context switch. Though a user level
scheduler has to call the kernel sometimes, experience
has shown this to be much less frequent.
Less checking of arguments. The kernel should not let
a buggy or malicious client mess up its invariants but
it is the norm for a buggy client to have the ability
to screw up its own libraries. Note that though it is
the norm it can cause problems and that is one reason
why some of us are pushing for safe languages, where
the features of the language that can screw up other
facilities are isolated (e.g. mfree, pointer
arithmetic, unchecked array dereferencing, passing
addresses of objects outside the scope where the object
is known to exist). This has been done with Modula-3
and we are working on doing this with C++.
-
Fewer data structures shared between all processors.
Since each process has its own ready queue the global
ready queue is not accessed so much. (This advantage
is not inherent but it is typical.)
-
Allows for scheduling policy to more easily be adjusted
on a per process basis as much of the policy lives in a
library.
The old disadvantages of user level schedulers have been
addressed a number of times, most completely by Anderson et
al. in their work on scheduler activations. I think this
was reported at the latest SOSP. The problems they address
include:
-
Page faults blocking the kernel thread being used by
the user level scheduler
-
Priorities not working between threads in different
processes
They have also designed an RPC system that for many
workloads works entirely at user level.
- Subjective
- Threads are a programming mechanism, like for
loops or procedures. They make it easier to write programs
that have certain attributes.
Processes are containers that are used to run a program on
many OSs, in particular Unix. The user of a program should
not have to care what particular implementation techniques
were used in the implementation of the program. Processes
act as firewalls between programs, allowing separate
processes to fail independently from each other.
The Plan 9 model solves the same problem that threads
address with coroutines plus processes forked with the
option to share nearly everything. Though the Plan 9
process model (a.k.a. Variable Weight Processes) allows for a
myriad of different types of processes sharing who knows
what with who knows whom, the actual patterns of sharing are
usually either of the thread variety (nearly everything) or
of the normal process variety (nearly nothing), anything
else is too confusing.
With a good implementation threads can have the power of a
process (independent blocking, ability to exploit
multiprocessors) and, in general, the weight of a coroutine.
- If process = program then handling errors and debugging is
much easier as the system knows the boundary of what should
be grouped together and
-
Killed when the program faults with a bad pointer
reference or whatever
-
Written into the same core file
-
Manipulated with the debugger
-
Manipulated by a user with ;SPM_quot;ps;SPM_quot; and ;SPM_quot;kill;SPM_quot;.
Threads inside processes allow this equation to hold much
more uniformly. RPC and nonshared memory (traditional)
process groups can sometimes cause this equation to break
down, but with much less frequency than it would in the Plan
9 model.