The second way is to write a library that is inherently a kernel-level implementation. It may define
all the same functions as in the first case, but these functions will be completely dependent upon
the existence of kernel routines to support them and may well be almost entirely in kernel space.
The user-level portion of the library will be relatively small compared to the amount of kernel-
level support it requires. The majority of library calls will require system calls.
Both of these methods can be used to implement exactly the same API, and they overlap in the
kinds of kernel support they require. Some implementations of the POSIX standard are of the first
kind, while both OS/2 and Win32 threads are of the second type. When Java is implemented on
these OSs it inherits the underlying behavior.
In either case, the programmer will use an API that is implemented by a threads library. That
library will provide a set of function calls (POSIX has about 50 calls, while Java has a dozen) that
is the programmer's sole interface to threads. Everything not provided by those calls must come
from the system's other libraries, meaning that 99% of writing a multithreaded program consists of
writing regular, old-fashioned code almost the same way as before.
As you read the descriptions of the APIs, you may be struck by the lack of fancy features. This is
intentional. These libraries provide a foundation for writing MT programs, but not every detail
you might like. They provide you the resources with which to build more elaborate functions. Spin
locks, priority-inheriting mutexes, deadlock-recovery features, etc., can be built out of these
primitives with relative ease. Thus, if you want very fast, minimal functionality constructs, they
are provided. If you want the slower, more complex constructs, you can build them.
We begin by talking about the parts of the system that are not inherently related to threads, but
that do define a great deal about how threads must work. We use the specific example of how
Solaris deals with the issues involved in building a viable interface between kernel-provided
functionality and the user-level threads requirements. Other operating systems and other libraries
have chosen different ways of providing this interface, and we do discuss them in general terms.
We believe that by understanding one implementation in detail, you will acquire the background
needed to fill in the gaps for the other implementations.
The Process Structure
The only thing the kernel knows about is the process structure. And the process structure has
changed (slightly) since you last looked at it in traditional multitasking operating systems such as
Figure 3-1. Process Structure in Traditional UNIX and in Solaris 2
Search WWH :