Está en la página 1de 4

Threads

Threading Models

Support for threads may be provided either at the user level, for user threads,
or by the kernel, for kernel threads. User threads are supported above the
kernel and are managed without kernel support, whereas kernel threads are
supported and managed directly by the operating system. Virtually all
contemporary operating systems including Windows, Linux, Mac OS X,
and Solaris support kernel threads. Ultimately, a relationship must exist
between user threads and kernel threads. In this section, we look at three
common ways of establishing such a relationship: the many-to-one model,
the one-to-one model, and the many-to- many model.

1.1

Many-to-One Model

The many-to-one model maps many user-level threads to one kernel


thread. Thread management is done by the thread library in user space,
so it is efficient. However, the entire process will block if a thread makes a
blocking system call. Also, because only one thread can access the kernel at
a time, multiple threads are unable to run in parallel on multicore systems.
Green threads a thread library available for Solaris systems and adopted
in early versions of Java used the many-to-one model. However, very few
systems continue to use the model because of its inability to take advantage
of multiple processing cores.

Figure 1: Many-to-One model

1.2

One-to-One Model

The one-to-one model maps each user thread to a kernel thread. It provides more concurrency than the many-to-one model by allowing another
thread to run when a thread makes a blocking system call. It also allows
multiple threads to run in parallel on multiprocessors. The only drawback to
this model is that creating a user thread requires creating the corresponding
kernel thread. Because the overhead of creating kernel threads can burden
the performance of an application, most implementations of this model restrict the number of threads supported by the system. Linux, along with
the family of Windows operating systems, implement the one-to-one model.

Figure 2: One-to-One model

1.3

Many-to-Many Model

The many-to-many model multiplexes many user-level threads to a smaller


or equal number of kernel threads. The number of kernel threads may
be specific to either a particular application or a particular machine (an
application may be allocated more kernel threads on a multiprocessor than
on a single processor).
Lets consider the effect of this design on concurrency. Whereas the
many- to-one model allows the developer to create as many user threads as
she wishes, it does not result in true concurrency, because the kernel can
schedule only one thread at a time. The one-to-one model allows greater
concurrency, but the developer has to be careful not to create too many
threads within an application (and in some instances may be limited in the
number of threads she can create). The many-to-many model suffers from
neither of these shortcomings: developers can create as many user threads
as necessary, and the corresponding kernel threads can run in parallel on a
multiprocessor. Also, when a thread performs a blocking system call, the
kernel can schedule another thread for execution.
2

Figure 3: Many-to-Many model

JVM Threads

The JVM is typically implemented on top of a host operating system. This


setup allows the JVM to hide the implementation details of the underlying
operating system and to provide a consistent, abstract environment that
allows Java programs to operate on any platform that supports a JVM.
The specification for the JVM does not indicate how Java threads are to be
mapped to the underlying operating system, instead leaving that decision to
the particular implementation of the JVM. For example, the Windows XP
operating system uses the one-to-one model; therefore, each Java thread for
a JVM running on such a system maps to a kernel thread. On operating
systems that use the many-to-many model (such as Tru64 UNIX), a Java
thread is mapped according to the many-to-many model. Solaris initially implemented the JVM using the many-to-one model (the green threads library,
mentioned earlier). Later releases of the JVM were implemented using the
many-to-many model. Beginning with Solaris 9, Java threads were mapped
using the one-to-one model. In addition, there may be a relationship between the Java thread library and the thread library on the host operating
system. For example, implementations of a JVM for the Windows family of
operating systems might use the Windows API when creating Java threads;
Linux, Solaris, and Mac OS X systems might use the Pthreads API.

Linux Threads

Linux provides the fork() system call with the traditional functionality
of duplicating a process. Linux also provides the ability to create threads
using the clone() system call. However, Linux does not distinguish between
processes and threads. In fact, Linux uses the term task rather than
process or thread when referring to a flow of control within a program.
3

When clone() is invoked, it is passed a set of flags that determine


how much sharing is to take place between the parent and child tasks. For
example, suppose that clone() is passed the flags CLONE_FS, CLONE_VM,
CLONE_SIGHAND, and CLONE_FILES. The parent and child tasks will then
share the same file-system information (such as the current working directory), the same memory space, the same signal handlers, and the same set of
open files. Using clone() in this fashion is equivalent to creating a thread as
described in this chapter, since the parent task shares most of its resources
with its child task. However, if none of these flags is set when clone() is
invoked, no sharing takes place, resulting in functionality similar to that
provided by the fork() system call.
The varying level of sharing is possible because of the way a task is represented in the Linux kernel. A unique kernel data structure (specifically,
struct task struct) exists for each task in the system. This data structure, instead of storing data for the task, contains pointers to other data structures
where these data are stored for example, data structures that represent the
list of open files, signal-handling information, and virtual memory. When
fork() is invoked, a new task is created, along with a copy of all the associated data structures of the parent process. A new task is also created
when the clone() system call is made. However, rather than copying all data
structures, the new task points to the data structures of the parent task,
depending on the set of flags passed to clone().

También podría gustarte