Os4 Threads

Download as pdf or txt
Download as pdf or txt
You are on page 1of 12

3 Threads

Each process has its address space and starts with a


single thread. Nevertheless, multiple threads run in the
same address space in the same process, as if they
were separate processes.

OPERATING SYSTEMS Why would anyone want to have a kind of process


within a process? There are several reasons for having
these miniprocesses, called threads.
THREADS

3.1 Thread Usage 3.1 Thread Usage


3 4

The main reason for having threads is that multiple The second advantage is that threads are easier to
activities are going on at once. To become simpler the create and destroy than processes. In many systems,
programming model, an application is decomposed creating a thread goes 10-100 times faster than
into multiple sequential threads that run in quasi- creating a process.
parallel.
A third reason is performance. Threads can yield
With threads, we add only a new element: the ability performance gain when there is substantial computing
for the parallel entities to share an address space and also substantial I/O, having threads allows these
and all of its data among themselves. activities to overlap, thus speeding up the application.
Finally, threads may supply real parallelism.

3.1 Thread Usage 3.1 Thread Usage


5 6

Suppose that the user suddenly deletes one sentence For the word processor is written as a two-threaded
from page 1 of an 800-page document. Then to program, one thread interacts with the user and the
make another change, he types a command to go to other handles reformatting in the background. As soon
page 600. The word processor is now forced to as the sentence is deleted from page 1, the interactive
reformat the entire book up to page 600 on the spot thread tells the reformatting thread to run the whole
because it does not know what the first line of page book. Meanwhile, the interactive thread continues to
600 will be until it has processed all the previous listen to the input devices and responds to simple
pages. There may be a sizeable delay before page commands like scrolling page 1 while the other thread
600 can be displayed, leading to an unhappy user. is computing madly in the background. If the
reformatting is completed before the request to see
page 600, it can be displayed without delay.

1
3.1 Thread Usage 3.1 Thread Usage
7 8

Many word processors have a feature of automatic If the program were single-threaded, then whenever
saving the entire file to disk every few minutes to a disk backup started, commands from the keyboard
protect the data. The third thread can handle the disk and mouse would be ignored until the backup was
backups without interfering with the other two. finished. The user would suppose the system as slow
performance. Alternatively, keyboard and mouse
events could interrupt the disk backup, allowing good
performance but leading to a complex interrupt-
driven programming model. With three threads, the
programming model is much simpler.

3.1 Thread Usage 3.1 Thread Usage


9 10

It should be clear that having three separate Now consider a web server. Requests for pages come
processes would not work here because all three in and the requested page is sent back. Some pages
threads need to operate on the document. By having are more commonly accessed than others. e.g.,
three threads instead of three processes, they share a Samsung's home page is accessed far more than a
common memory and thus all have access to the same page containing the technical specifications of any
document being edited. particular camcorder. Web servers use this fact to
improve performance by maintaining a collection of
heavily used pages in main memory. Such a collection
is called a cache.

3.1 Thread Usage 3.1 Thread Usage


11 12

In figure, one thread (the dispatcher) reads incoming When the worker wakes up, it checks if the request
requests for work from the network. After examining can be satisfied from the cache. If not, it starts a read
the request, it chooses an idle worker thread and operation to get the page from the disk and blocks
hands it the request. The dispatcher then moves until the disk operation completes. When the thread
sleeping worker from blocked state to ready state. blocks on the disk operation, another thread is chosen
to run, possibly the dispatcher, in order to acquire
more work, or possibly another worker that is now
ready to run.

2
3.1 Thread Usage 3.1 Thread Usage
13 14

A rough outline of the code is given in figure. Here, Consider how the Web server could be written in the
TRUE is assumed to be the constant 1. Also, buf and absence of threads or with only a single thread. The
page are structures appropriate for holding a work main loop of the Web server gets a request, examines
request and a web page, respectively. it, and carries it out to completion before getting the
next one. While waiting for the disk, the server is idle
and does not process any other incoming requests. If
the Web server is running on a dedicated machine,
the CPU is simply idle while the Web server is Waiting
for the disk. The net result is that many fewer
requests/sec can be processed. Thus threads gain
considerable performance.

3.1 Thread Usage 3.1 Thread Usage


15 16

So far we have seen two possible designs: a The server records the state of the current request in a
multithreaded and a single-threaded Web server. table and then goes and gets the next event. The next
Suppose that threads are not available but the system event may either be a request for new work or a
designers find the performance loss due to single reply from the disk about a previous operation. If it is
threading unacceptable. If a nonblocking version of new work, that work is started. If it is a reply from the
the read system call is available, a third approach is disk, the relevant information is fetched from the table
possible. When a request comes in, the one and only and the reply processed. With nonblocking disk I/O, a
thread examines it. If it can be satisfied from the reply probably will have to take the form of a signal
cache, fine, but if not, a nonblocking disk operation is or interrupt.
started.

3.1 Thread Usage 3.1 Thread Usage


17 18

In this design, the "sequential process" model that we A third example is applications used very large
had in the first two cases is lost. The state of the amounts of data. The normal approach is to read in a
computation must be explicitly saved and restored in block of data, process it, and then write it out again.
the table every time the server switches from working The problem here is that if only blocking system calls
on one request to another. In effect, we are simulating are available, the process blocks while data are
the threads and their stacks the hard way. A design coming in and data are going out. Having the CPU go
like this, in which each computation has a saved state, idle when there is lots of computing to do is clearly
and there exists some set of events that can occur to wasteful and should be avoided if possible.
change the state is called a finite-state machine. This
concept is widely used throughout computer science.

3
3.1 Thread Usage 3.2 The Classical Thread Model
19 20

Threads offer a solution. The process could be One important concepts in the process model is a
structured with an input thread, a processing thread, thread of execution. The thread has a program
and an output thread. The input thread reads data counter that follows which instruction to execute next. It
into an input buffer. The processing thread takes data has registers, which hold its current working variables.
out of the input buffer, processes them, and puts the It has a stack, which contains the execution history,
results in an output buffer. The output buffer writes with one frame for each procedure called but not yet
these results back to disk. In this way, input, output, returned from. Although a thread must execute in
and processing can all be going on at the same time. some process, the thread and its process are different
Of course, this model only works if a system call concepts and can be treated separately. Processes
blocks only the calling thread, not the entire process. are used to group resources together; threads are the
entities scheduled for execution on the CPU.

3.2 The Classical Thread Model 3.2 The Classical Thread Model
21 22

What threads add to the process model is to allow In figure (a) we see three traditional processes. Each process has
multiple executions to take place in the same process its own address space and a single thread of control. In contrast,
in figure (b) we see a single process with three threads of control.
environment. Having multiple threads running in Although in both cases we have three threads, in figure (a) each
parallel in one process is similar to having multiple of them operates in a different address space, whereas in figure
processes running in parallel in one computer. While (b) all three of them share the same address space.
the threads share an address space and other
resources, processes share physical memory, disks,
printers, and other resources. Because threads have
some of the properties of processes, they are
sometimes called lightweight processes.
(a) Three processes each with one thread. (b) One process with three threads.

3.2 The Classical Thread Model 3.2 The Classical Thread Model
23 24

When a multithreaded process is run on a single-CPU Different threads are not as independent as different
system, the threads take turns running. By switching processes. All threads have the same address space,
back and forth among multiple processes, the system set of open files, child processes, alarms, and signals.
gives the illusion of separate sequential processes Since every thread can access every memory address
running in parallel. Multithreading works the same within the process' address space, one thread can
way. With three compute-bound threads in a process, read, write, or even destroy another thread's stack.
the threads would appear to be running in parallel, There is no protection between threads because (1) it
each one on a CPU with one-third the speed of the is impossible, and (2) it should not be necessary. A
real CPU. process is always owned by a single user, who has
created multiple threads so that they can cooperate,
not fight.

4
3.2 The Classical Thread Model 3.2 The Classical Thread Model
25 26

The first column lists some items shared by all threads Like a process with only one thread, a thread can be
in a process. The second one lists some items private to in any one of several states: running, blocked, ready,
each thread. or terminated. A running thread currently has the CPU
and is active. A blocked thread is waiting for some
event to unblock it. For example, when a thread
performs a system call to read from the keyboard, it
is blocked until input is typed. A thread can block
waiting for some external event to happen or for
some other thread to unblock it. A ready thread is
scheduled to run and will as soon as its turn comes up.

3.2 The Classical Thread Model 3.2 The Classical Thread Model
27 28

It is important to realize that each thread has its own When multithreading is present, processes normally
stack, as illustrated in figure below. Each thread's stack start with a single thread. This thread has the ability
contains one frame for each procedure called but not to create new threads by calling a library procedure,
yet returned from. This frame contains the procedure's for example, thread_create. A parameter to
local variables and the return address to use when the thread_create specifies the name of a procedure to
procedure call has finished. run. It is not possible to specify anything about the
new thread's address space, since it automatically runs
in the address space of the creating thread.
Sometimes threads are hierarchical, with a parent-
child relationship, but often no such relationship exists,
with all threads being equal.

3.2 The Classical Thread Model 3.2 The Classical Thread Model
29 30

When a thread has finished its work, it can exit by Another common thread call, thread_yield, allows a
calling a library procedure, say, thread_exit. It then thread to voluntarily give up the CPU to let another
disappears and is no longer schedulable. In some thread run. Such a call is important because there is
thread systems, one thread can wait for a specific no interrupt to enforce multiprogramming as there is
thread to exit by calling a procedure, for example, with processes. Thus it is important for threads to be
thread_join. This procedure blocks the calling thread polite and voluntarily abandon the CPU from time to
until a specific thread has exited. In this regard, time to give other threads a chance to run. Other calls
thread creation and termination is very much like allow one thread to wait for another thread to finish
process creation and termination, with the same some work, for a thread to announce that it has
options as well. finished some work.

5
3.2 The Classical Thread Model 3.2 The Classical Thread Model
31 32

Besides threads introduce a number of problems into Another class of problems is related to the fact that
the programming model. To start with, consider the threads share many data structures.
effects of the fork system call.  What happens if one thread closes a file while another one is
 If the parent process has multiple threads, should the child also still reading from it? Suppose that one thread notices that
have them? If not, the process may not work properly, since all there is too little memory and starts allocating more memory.
of them may be essential. However, if the child process gets as Just then, a thread switch occurs, and the new thread also
many threads as the parent, what happens if a thread in the notices that there is too little memory and also starts allocating
parent was blocked on a read call, say, from the keyboard? more memory. Memory will probably be allocated twice.
Are two threads now blocked on the keyboard, one in the These problems can be solved with some effort, but careful
parent and one in the child? When a line is typed, do both thought and design are needed to make multithreaded
threads get a copy of it? Only the parent? Only the child? programs work correctly.

3.3 POSIX Threads 3.3 POSIX Threads


33 34

IEEE has defined a standard (IEEE1003.1c) for threads The calls we will describe are listed as follow.
to write portable threaded programs. The threads Thread call Description
package is called Pthreads. Most UNIX systems Pthread_create Create a new thread
support it. The standard defines over 60 function calls. Pthread_exit Terminate the calling thread
Instead we will just describe a few of the major ones
Pthread_join Wait for a specific thread to exit
to give an idea of how it works.
Pthread_yield Release the CPU to let another
thread run

Pthread_attr_init Create and initialize a thread's


attribute structure

Pthread_attr_destroy Remove a thread's attribute structure

3.3 POSIX Threads 3.4 Implementing in User Space


35 36

#include <stdio.h>
There are two main ways to implement a threads
#include <sys/syscall.h>
#include <pthread.h>
package: in user space and in the kernel. The first
void *myID() method is to put the threads package entirely in user
{ space. The kernel knows nothing about them. The most
printf(“I am a thread.”); obvious advantage is that a user-level threads
}
main()
package can be implemented on an operating system
{ that does not support threads. With this approach,
pthread_t thred; threads are implemented by a library.
int th_no;
th_no = pthread_create(&thred, NULL, myID, NULL)
pthread_exit(&thred);
}

6
3.4 Implementing in User Space 3.4 Implementing in User Space
37 38

All of these implementations have the same general The threads run on top of a run-time system, which is
structure, which is illustrated in figure. a collection of procedures that manage threads. We
have seen four of these already: pthread_create,
pthread_exit, pthread_join, and pthread_yield.

(a) A user-level threads package. (b) A threads package managed by the kernel. (a) A user-level threads package. (b) A threads package managed by the kernel.

3.4 Implementing in User Space 3.4 Implementing in User Space


39 40

When threads are managed in user space, each When a thread does something that may cause it to
process needs its own private thread table to keep become blocked locally, it calls a run-time system
track of them. This table is similar to the kernel's procedure. This procedure checks that if the thread is
process table, except that it keeps track only of the in blocked state. If so, it stores the its own registers in
per-thread properties, such as each thread's program the thread table, looks in the table for a ready thread
counter, stack pointer, registers, state, and so forth. to run, and reloads the machine registers with the new
The thread table is managed by the run-time system. thread's saved values. As soon as the stack pointer
When a thread is moved to ready state or blocked and program counter have been switched, the new
state, the information needed to restart it is stored in thread comes to life again automatically.
the thread table, exactly the same way as ones in the
process table.

3.4 Implementing in User Space 3.4 Implementing in User Space


41 42

However, there is one key difference with processes. Besides, user-level threads allow each process to have
When a thread calls thread_yield, the code of its own customized scheduling algorithm. For some
thread_yield can save the thread's information in the applications, for example, those with a garbage
thread table itself. Then it can call the thread collector thread, not having to worry about a thread
scheduler to pick another thread to run. The being stopped at an inconvenient moment is a plus.
procedure that saves the thread's state and the They also scale better, since kernel threads always
scheduler are just local procedures, so invoking them is require some table space and stack (space in the
much more efficient than making a kernel call. Among kernel which can be a problem if there are a very
other issues, no trap is needed, no context switch is large number of threads).
needed, the memory cache need not be flushed, and
so on. This makes thread scheduling very fast.

7
3.4 Implementing in User Space 3.4 Implementing in User Space
43 44

Despite their better performance, user-level threads The system calls could all be changed to be
packages have some major problems. First among nonblocking (e.g., a read on the keyboard would just
these is the problem of how blocking system calls are return 0 bytes if no characters were already
implemented. Suppose that a thread reads from the buffered), but requiring changes to the operating
keyboard. Letting the thread make the system call is system is unattractive. Besides, one of the arguments
unacceptable, since this will stop all the threads. One for user-level threads was precisely that they could
of the main goals was to allow each one to use run with existing operating systems. In addition,
blocking calls, but to prevent one blocked thread from changing the semantics of read will require changes
affecting the others. With blocking system calls, it is to many user programs.
hard to see how this goal can be achieved.

3.4 Implementing in User Space 3.4 Implementing in User Space


45 46

Somewhat analogous to the problem of blocking Another problem with user-level thread packages is
system calls is the problem of page faults. Briefly, that if a thread starts running, no other thread in that
computers may not load all of a program in main process will ever run unless the first thread voluntarily
memory at once. If the program jumps to an instruction gives up the CPU. Within a single process, there are
that is not in memory, a page fault occurs and the no clock interrupts, making it impossible to schedule
operating system will get the missing instruction from processes round-robin fashion (taking turns). Unless a
disk. This is called a page fault. The process is thread enters the run-time system of its own free will,
blocked while the necessary instruction is being the scheduler will never get a chance.
located. If a thread causes a page fault, the kernel
naturally blocks the entire process until the disk I/O is
complete, even though other threads maybe runnable.

3.4 Implementing in User Space 3.4 Implementing in User Space


47 48

One possible solution to the problem of threads Another, and really the most destroying, argument
running forever is to have the run-time system request against user-level threads is that programmers
a clock signal (interrupt) once a second to give it generally want threads precisely in applications
control, but this, too, is crude and messy to program. where the threads block often. These threads are
Periodic clock interrupts at a higher frequency are not constantly making system calls. Once a trap has
always possible, and even if they are, the total occurred to the kernel to carry out the system call, it is
overhead may be substantial. Furthermore, a thread hardly any more work for the kernel to switch threads
might also need a clock interrupt, interfering with the if the old one has blocked, and having the kernel do
run-time system's use of the clock. this eliminates the need for constantly making select
system calls that check to see if read system calls are
safe.

8
3.5 Implementing in the Kernel 3.5 Implementing in the Kernel
49 50

Now let us consider having the kernel know about and The kernel's thread table holds each thread's
manage the threads. No run-time system is needed in registers, state, and other information. The information
each. Also, there is no thread table in each process. is the same as with user-level threads, but now kept in
Instead, the kernel has a thread table that keeps the kernel instead of in user space (inside the run-time
track of all the threads in the system. When a thread system). This information is a subset of the information
wants to create a new thread or destroy an existing that traditional kernels maintain about their single
thread, it makes a kernel call, which then does the threaded processes, that is, the process state. In
creation or destruction by updating the kernel thread addition, the kernel also maintains the traditional
table. process table to keep track of processes.

3.5 Implementing in the Kernel 3.5 Implementing in the Kernel


51 52

All calls that might block a thread are implemented as Due to the relatively greater cost of creating and
system calls, at considerably greater cost than a call destroying threads in the kernel, some systems take an
to a run-time system procedure. When a thread environmentally correct approach and recycle their
blocks, the kernel can run either another thread in threads. When a thread is destroyed, it is marked as
ready from the same process or a thread from a not runnable, but its kernel data structures are not
different process. With user-level threads, the run-time otherwise affected. Later, when a new thread must be
system keeps running threads from its own process created, an old thread is reactivated, saving some
until the kernel takes the CPU away from it (or there overhead. Thread recycling is also possible for user-
are no ready threads left to run). level threads, but since the thread management
overhead is much smaller, there is less incentive to do
this.

3.5 Implementing in the Kernel 3.5 Implementing in the Kernel


53 54

Kernel threads do not require any new, nonblocking While kernel threads solve some problems, they do
system calls. In addition, if one thread in a process not solve all problems. For example, what happens
causes a page fault, the kernel can easily check to when a multithreaded process forks? Does the new
see if the process has any other runnable threads, and process have as many threads as the old one did, or
if so, run one of them while waiting for the required does it have just one? In many cases, the best choice
page to be brought in from the disk. Their main depends on what the process is planning to do next. If
disadvantage is that the cost of a system call is it is going to call exec to start a new program,
substantial, so if thread operations (creation, probably one thread is the correct choice, but if it
termination, etc.) are common, much more overhead continues to execute, reproducing all the threads is
will be incurred. probably the right thing to do.

9
3.5 Implementing in the Kernel 3.6 Hybrid Implementations
55 56

Another issue is signals. Remember that signals are To combine the advantages of user-level threads with
sent to processes, not to threads, at least in the kernel-level threads, one way is use kernel-level
classical model. When a signal comes in, which thread threads and then multiplex user-level threads onto
should handle it? Possibly threads could register their some or all of the kernel threads as shown in figure.
interest in certain signals, so when a signal came in it
would be given to the thread that said it wants it. But
what happens if two or more threads register for the
same signal. These are only two of the problems
threads introduce, but there are more.

Multiplexing user-level threads onto kernel-level threads.

3.6 Hybrid Implementations 3.6 Hybrid Implementations


57 58

When this approach is used, the programmer can With this approach, the kernel is aware of only the
determine how many kernel threads to use and how kernel-level threads and schedules those. Some of
many user-level threads to multiplex on each one. those threads may have multiple user-level threads
multiplexed on top of them. These user-level threads
are created, destroyed, and scheduled just like user-
level threads in a process that runs on an operating
system without multithreading capability. In this model,
each kernel-level thread has some set of user-level
threads that take turns using it.

Multiplexing user-level threads onto kernel-level threads.

3.7 Scheduler Activations 3.7 Scheduler Activations


59 60

Although kernel threads are better than user-level When a hardware interrupt occurs while a user thread
threads in some key ways, they are slower. The goals is running, the CPU switches into kernel mode. When
of the scheduler activation work are to mimic the the interrupt handler has finished, if the process is not
functionality of kernel threads, but with the better interested in the interrupt, it puts the interrupted
performance and greater flexibility in user space. thread back in its before state; but if the process is
Efficiency is achieved by avoiding unnecessary interested in the interrupt, the interrupted thread is
transitions between user and kernel space. not restarted. Instead, the run-time system is started
on that virtual CPU, with the state of the interrupted
thread. It is then up to the run-time system to decide
which thread to schedule on that CPU: the interrupted
one, the newly ready one, or some third choice.

10
3.8 Pop-Up Threads 3.8 Pop-Up Threads
61 62

Threads are frequently useful in distributed systems. Such a thread is called a pop-up thread and is
An important example is how incoming messages, for illustrated in figure.
example requests for service, are handled. The
traditional approach is to have a process or thread
that is blocked on a receive system call waiting for an
incoming message. When a message arrives, it
accepts the message and processes it. However, a
completely different approach is also possible, in
which the arrival of a message causes the system to
create a new thread to handle the message.
Creation of a new thread when a message arrives. (a) Before the message arrives.
(b) After the message arrives.

3.8 Pop-Up Threads 3.9 Single-Thread to Multithread


63 64

A key advantage of pop-up threads is that since they Converting programs written as a single-threaded to
are brand new, they don’t have any history (registers, multithreading requires some tricks. As a start, the
stack, etc.). This makes it possible to create such a code of a thread normally consists of multiple
thread quickly. The new thread is given the incoming procedures, just like a process. These may have local
message to process. The result of using pop-up variables, global variables, and parameters. Local
threads is that the latency between message arrival variables and parameters do not cause any trouble,
and the start of processing can be made very short. but variables that are global to a thread but not
global to the entire program are a problem. These
are variables that are global in the sense that many
procedures within the thread use them, but other
threads should logically leave them alone.

3.9 Single-Thread to Multithread 3.9 Single-Thread to Multithread


65 66

In figure, Thread1 executes the system call access to


find out if it has permission to access a certain file.
The operating system returns the answer in the global
variable errno. After control has returned to Thread1,
but before it has a chance to read errno, the
scheduler decides that Thread1 has had enough CPU
time for the moment and decides to switch to Thread2.
Thread2 executes an open call that fails, which causes
errno to be overwritten and Thread1's access code to
be lost forever. When Thread1 starts up later, it will
Conflicts between threads over the use of a global variable
read the wrong value and behave incorrectly.

11
3.9 Single-Thread to Multithread 3.9 Single-Thread to Multithread
67 68

The next problem is that many library procedures are Next, consider signals. Some signals are logically
not reentrant. That is, they were not designed thread specific, whereas others are not. For example,
to have a second call made to any given procedure if a thread calls alarm, it makes sense for the resulting
while a previous call has not yet finished. signal to go to the thread that made the call.
However, when threads are implemented entirely in
user space, the kernel does not even know about
Similarly, memory allocation procedures maintain threads and can hardly direct the signal to the right
crucial tables about memory usage. If a thread switch one.
occurs while the tables are inconsistent and a new call
comes in from a different thread, an invalid pointer
may be used, leading to a program crash.

3.9 Single-Thread to Multithread


69

One last problem introduced by threads is stack


management. In many systems, when a process' stack
overflows, the kernel just provides that process with
more stack automatically. When a process has
multiple threads, it must also have multiple stacks. If
the kernel is not aware of all these stacks, it cannot
grow them automatically upon stack fault.

12

You might also like