
The Solaris 2.5
Threads Library
Overview
Solaris 2.5 provides an implementation of the many-to-many model
[Powell et al. 1991] and introduces a new set of vocabulary: a kernel
thread in Solaris is referred to as a lightweight process
(LWP), while a user thread is simply a thread. The Solaris
threads package is intended to isolate the programmer as much as
possible from the notion of LWPs.
You may wish to get some background information on other threads package implementation models
before proceeding.
User-level Thread Scheduling
User-level Thread States
Unbound threads in Solaris may be in one five states: Stopped,
Blocked, Run queue, Dispatchable or On
LWP. A thread that has been suspended is Stopped, while a
thread blocked on a synchronization primitive is Blocked. If a
thread is runnable but is not running on an LWP, then it is either on
the Run queue or, if an LWP has been found to run the thread,
it is Dispatchable. Once a runnable thread is picked up by an
LWP, it is On LWP. While a thread cannot be actually running on
a CPU unless it is On LWP, being On LWP does not imply
that a thread is running on a CPU; the underlying LWP itself could be
sleeping, waiting for a processor, etc.
Thread-LWP Interaction
Solaris implements the multiplexing of user-level threads onto LWPs by
maintaining a pool of LWPs. Any unbound thread may run on any
LWP in the pool; when a thread is ready to run (i.e. in the user-level
run queue), the user-level scheduler takes an LWP out of the pool and
assigns it to run the newly runnable thread (changing the thread's
state to On LWP). This LWP will continue to run the thread
until either a thread at a higher priority becomes runnable or the
thread blocks on a synchronization primitive. Thus, the user-level
threads library is nonpreemptable when all threads have the
same priority.
When an LWP is idle (i.e. the LWP is in the pool and no threads are
runnable), the user-level scheduler parks it in the
kernel. If a thread becomes runnable while LWPs are parked, the
user-level scheduler unparks one of the LWPs. Once an LWP is unparked,
it dequeues and runs a user thread from the user-level run queue.
LWP Pool Management
The size of the LWP pool has a critical impact on the performance of
the many-to-many model: if the number of LWPs in the pool is nearly
equal to the number of threads, the implementation will act much like
the one-to-one model. Conversely, if there are very few LWPs in the
pool, the implementation will act like the many-to-one model.
Of particular concern is the risk of deadlock with an excessively
small pool: one thread may block on a resource in the kernel and go to
sleep, and by so doing block the LWP needed to run the
resource-holder. To solve this problem, the threads package makes a
minimal guarantee to the threads programmer: progress will always be
made. This is implemented through the use of the SIGWAITING
signal. When the kernel realizes that all of a process's LWPs are
blocked at the kernel level, it drops a SIGWAITING on the
process. Upon receipt of the signal, the user-level threads package
decides whether or not to create a new LWP, on the basis of the number
of runnable threads. The SIGWAITING mechanism makes no
guarantees about optimal use of LWPs on a multiprocessor.
Specifically, a process may have many more runnable user-level threads
than it has LWPs, but it does not receive a SIGWAITING until
all LWPs are blocked. Thus, even if there are processors available and
work to be done, the SIGWAITING mechanism does not guarantee
that there is a sufficient number of LWPs to run the user threads on
the available processors. If the programmer wishes to use unbound
threads and take advantage of all available processors, he or she is
required to advise the library on the number of LWPs required.
[
Top |
Introduction |
Solaris |
Threadmon |
References
]
The text of this web document was taken from a paper by Bryan M. Cantrill and Thomas W. Doeppner Jr., Department
of Computer Science, Brown University, Providence, RI 02912-1910
Greg Foxman (gmf@cs.brown.edu)