Solaris and Windows NT both support powerful multithreading/multiprocessing to help get the job done faster
Shashi Prasad
A symmetric multiprocessor (SMP), also called a shared-memory multiprocessor, has multiple CPUs sharing a common memory space. This memory sharing allows a single copy of the OS to be running on all the processors. SMP machines promise higher performance and better scalability, but the OS software must be designed to support multiprocessing and also be tuned for a scalable and efficient SMP. The OS, by means of some application programming interface (API), should allow the application developer to split his or her program into multiple pieces and run them simultaneously.
Loose Threads
A sequential proces
s has a single flow of control, a sequence of instructions executed by the process. In a multithreaded process, there are multiple schedulable flows of control called threads. Threads are associated with a sequence of instructions and are often referred to as "threads of execution." Threads support the notion of concurrent programming and are used to exploit the inherent hardware parallelism of the SMP machine. By splitting a problem into smaller pieces, a thread can run each piece concurrently on an SMP machine.
Traditionally, applications were split into multiple processes, and some form of interprocess communications (IPC) was used to communicate between the processes. A multithreaded process has more than one thread of control sharing both address space and resources. Using threads eliminates the need for any IPC and reduces context-switching overhead. Threads are also referred to as lightweight processes (LWPs), since the context maintained for threads is much lighter than processes. Even though th
reads are schedulable entities, it does not necessarily mean that they run in parallel. Threads can be scheduled concurrently on multiple processors, while on uniprocessor machines, threads are time-sliced.
With Solaris 2.2, Sun introduced a threads library for applications developers to take advantage of the multiprocessing and multithreading features of the advanced kernel. Solaris uses a two-tier thread library model. At the top layer are the user threads, and at the bottom layer are LWPs.
LWPs are kernel threads -- they use kernel resources and are the actual schedulable entities in Solaris. The kernel is aware only of LWPs and knows nothing about user threads. The user threads are managed by the threads library, which supports one-to-one, many-to-many, or many-to-one mapping between the user threads and the LWPs (see the figure
"Thread Mapping in Solaris and NT"
). The multiplexing of user threads to a pool of LWPs and the scheduling of user threads is handled by the thread
s library. Though the two-tier thread model adds some extra responsibility onto the user application, it allows for the cheap creation of user threads without using a lot of system resources.
Solaris has bound and unbound user threads. A bound thread is a one-to-one mapping between a user thread and an LWP. An unbound thread does not have a dedicated LWP; the threads library schedules the user thread on a pool of LWPs in a process. Applications that require global scheduling with respect to other LWPs in the system should use bound threads.
LWPs are scheduled by the kernel according to their scheduling class and priority. In Solaris, the process is created with an initial LWP, which inherits the scheduling class and priority of the parent process. Bound threads inherit the scheduling class and priority of the underlying LWP, while unbound threads inherit the scheduling class and priority of the process.
The Solaris kernel employs a preemptive priority-based scheduling system. LWPs of higher
priority run before LWPs of lower priority. The Solaris threads library schedules user threads using priority levels on a pool of LWPs and chooses an LWP to execute user threads that are ready to run. If the LWP is blocked on an indefinite wait, the library saves the context of the blocked thread and assigns another thread to the LWP to run. The threads library usually creates enough LWPs to ensure that the process can proceed without an indefinite wait.
A Different Approach
Microsoft Windows NT was designed from the ground up to support multiprocessing and multithreading. Borrowing from the ideas of object-oriented design, Windows NT uses object classes to represent the system resources. In NT, both processes and threads are represented as objects. Threads are the schedulable entities in NT; every process in NT must have at least one thread before it can execute. Unlike the Solaris
two-tier thread model
, NT uses a straightforward one-to-one mapping between a
user thread object and a kernel thread object.
Threads have 32 different priority levels and are broadly classified into two classes: real-time and variable. Real-time threads in NT are always scheduled ahead of other threads in the system, and the NT kernel does not alter the priority of real-time threads. The thread dispatcher uses a preemptive priority scheduler, with the highest priority thread always scheduled to run.
Threads in the variable class have a base and a dynamic priority. The base priority of a thread ranges two levels above and below the process base priority. The kernel periodically adjusts a thread's dynamic priority. For example, when a thread waits on an I/O, the kernel raises the dynamic priority of that thread. Threads that are CPU-bound tend to have lower dynamic priorities, while I/O-bound threads tend to have higher dynamic priority. A thread's dynamic priority can never fall below the thread's base priority.
Each process has a processor affinity, a set of processor
s on which the threads of that process can run. Processor affinity affects thread scheduling. The NT kernel first picks the highest priority thread and then determines if the thread can run on the processor. If it can't, the next highest priority thread on the processor is chosen to execute. NT, like Solaris, employs soft processor affinity such that it always tries to schedule a thread on the processor on which it last ran.
Threads API
In addition to the SMP OSes, both systems provide an API for creating and using threads in an application. Solaris's threads API is based on the Unix International threads interface, and support for the Posix threads interface is planned in the upcoming release of Solaris 2.5. The Solaris threads interface is very similar to the Posix interface, and applications developed using the threads API on Solaris can be easily ported to use the Posix interface. Windows NT does not support the Posix interface, and applications use the Win32 interface to de
velop multithreaded applications.
Any threads API can be broadly classified into such groups as thread management, thread synchronization, and thread-specific data. In both Solaris and NT, an initial thread is created when the process starts execution. Additional threads of control must be created by calling the thread-creation API. When a thread is created, it begins executing a start routine, and the new thread of control within the process is capable of independently being scheduled in both Solaris and Windows NT. Each newly created thread has its own stack, program counter, and a thread identifier. Thread identifiers in Windows NT are unique system-wide, while in Solaris they are guaranteed to be unique only within the context of the executing process.
Threads in a process exist in the same address space and share all the process resources, such as data variables, open file descriptors, and object handles. The sharing of data can be a blessing in disguise. Consider two threads in a multiproces
sing environment concurrently updating a linked list. The results of such operations are undefined and can lead to data corruption. In a multithreaded program, any data that multiple threads can update simultaneously must be synchronized. Both Solaris and NT provide several thread-synchronization primitives.
Solaris and NT provide mutual exclusion, or mutex locks, to manage critical sections of the code. These mutex locks allow only one thread to execute the critical section of the code.
In multithreaded applications, it is common to divide the work between multiple threads. In such cases, one thread might have to wait for another thread to reach a particular state before proceeding. This form of synchronization is often called event synchronization. Solaris provides condition variables, while NT provides event objects for interthread synchronization.
In addition to mutexes and condition variables, Solaris provides semaphores and reader/writer locks for thread synchronization. Some of the ot
her synchronization primitives in NT are critical section objects, semaphores, and I/O completion ports. The synchronization APIs on NT offer better functionality than those on Solaris and, except for critical section objects, provide the same APIs for process and thread synchronization. Though both OSes provide the basic functions for thread programming, the Posix threads interface provides much more advanced features than does the Win32 interface.
Developing multithreaded applications also requires an environment that supports multithreading. Solaris provides re-entrant versions for most of the commonly used libraries. Currently, Solaris does not provide thread-safe versions for Motif and OpenLook libraries, which are rarely used by multiple threads in a program. Windows NT also provides re-entrant versions for most of its commonly used libraries.
Debugging multithreaded applications is a big challenge and could be frustrating without the support of a thread-aware debugger. Solaris supports a mu
ltithreaded debugger as a part of SPARCworks/iMPact, while Microsoft supports a multithreaded NT debugger as part of Visual C++. In addition to showing all threads of a process, both debuggers support suspending and resuming threads and inspecting variables on a per-thread basis.
The structured exception-handling (SEH) feature of Windows NT aids in the development of robust multithreaded applications. Terminating threads can always release held resources before exiting on exceptions, thus avoiding indefinite postponement. The exception-handling features of Windows NT are not language-specific.
Multithreaded Future
Solaris and Windows NT provide good environments for the development of multithreaded applications. They are by no means the only multithreaded OSes available at the desktop. NextStep, AIX (and other Unixes), and OS/2 provide thread support, as does Windows 95. Future versions of the Macintosh OS will also go threaded. The increasing availability of multiprocesso
r hardware and the improved responsiveness realized even on uniprocessor machines means multithreaded applications are here to stay.
Solaris NT
==============================
Thread model Two-tier Direct
Posix thread No (planned No
API support for 2.5)
Re-entrant OS libraries Yes (except for Yes
(i.e., thread-safe) Motif/OpenLook)
illustration_link (6 Kbytes)

Solaris's various API-to-kernel thread mappings is flexible, but NT's direct mapping is simpler.
shaship@anstec.com
or on BIX at
editors@bix.com
.