Vous êtes sur la page 1sur 19

OVERVIEW and HISTORY

What is an operating system? Hard to define precisely, because operating systems


arose historically as people needed to solve problems associated with using
computers.
Much of operating system history driven by relative cost factors of hardware
and people. Hardware started out fantastically expensive relative to people
and the relative cost has been decreasing ever since. Relative costs drive the
goals of the operating system.
o In the beginning Expensive Hardware, Cheap People !oal
maximi"e hardware utili"ation.
o #ow Cheap Hardware, Expensive People !oal ma$e it easy for
people to use computer.
In the early days of computer use, computers were huge machines that are
expensive to buy, run and maintain. %omputer used in single user, interactive
mode. &rogrammers interact with the machine at a very low level ' flic$
console switches, dump cards into card reader, etc. (he interface is basically
the raw hardware.
o &roblem %ode to manipulate external I)* devices. Is very complex,
and is a ma+or source of programming difficulty.
o ,olution -uild a subroutine library .device drivers/ to manage the
interaction with the I)* devices. (he library is loaded into the top of
memory and stays there. (his is the first example of something that
would grow into an operating system.
-ecause the machine is so expensive, it is important to $eep it busy.
o &roblem computer idles while programmer sets things up. &oor
utili"ation of huge investment.
o ,olution Hire a speciali"ed person to do setup. 0aster than
programmer, but still a lot slower than the machine.
o ,olution -uild a batch monitor. ,tore +obs on a dis$ .spooling/, have
computer read them in one at a time and execute them. -ig change in
computer usage debugging now done offline from print outs and
memory dumps. #o more instant feedbac$.
o &roblem 1t any given time, +ob is actively using either the %&2 or an
I)* device, and the rest of the machine is idle and therefore unutili"ed.
o ,olution 1llow the +ob to overlap computation and I)*. -uffering and
interrupt handling added to subroutine library.
o &roblem one +ob can3t $eep both %&2 and I)* devices busy. .Have
compute'bound +obs that tend to use only the %&2 and I)*'bound +obs
that tend to use only the I)* devices./ !et poor utili"ation either of
%&2 or I)* devices.
o ,olution multiprogramming ' several +obs share system. 4ynamically
switch from one +ob to another when the running +ob does I)*. -ig
issue protection. 4on3t want one +ob to affect the results of another.
- 1 -
Memory protection and relocation added to hardware, *, must
manage new hardware functionality. *, starts to become a significant
software system. *, also starts to ta$e up significant resources on its
own.
&hase shift %omputers become much cheaper. &eople costs become
significant.
o Issue It becomes important to ma$e computers easier to use and to
improve the productivity of the people. *ne big productivity sin$
having to wait for batch output .but is this really true?/. ,o, it is
important to run interactively. -ut computers are still so expensive
that you can3t buy one for every person. ,olution interactive
timesharing.
o &roblem *ld batch schedulers were designed to run a +ob for as long
as it was utili"ing the %&2 effectively .in practice, until it tried to do
some I)*/. -ut now, people need reasonable response time from the
computer.
o ,olution &reemptive scheduling.
o &roblem &eople need to have their data and programs around while
they use the computer.
o ,olution 1dd file systems for 5uic$ access to data. %omputer becomes
a repository for data, and people don3t have to use card dec$s or tapes
to store their data.
o &roblem (he boss logs in and gets terrible response time because the
machine is overloaded.
o ,olution &rioriti"ed scheduling. (he boss gets more of the machine
than the peons. -ut, %&2 scheduling is +ust an example of resource
allocation problems. (he timeshared machine was full of limited
resources .%&2 time, dis$ space, physical memory space, etc./ and it
became the responsibility of the *, to mediate the allocation of the
resources. ,o, developed things li$e dis$ and physical memory 5uotas,
etc.
*verall, time sharing was a success. However, it was a limited success. In
practical terms, every timeshared computer became overloaded and the
response time dropped to annoying or unacceptable levels. Hard'core hac$ers
compensated by wor$ing at night, and we developed a generation of pasty'
loo$ing, unhealthy insomniacs addicted to caffeine.
%omputers become even cheaper. It becomes practical to give one computer
to each user. Initial cost is very important in mar$et. Minimal hardware .no
networ$ing or hard dis$, very slow microprocessors and almost no memory/
shipped with minimal *, .M,'4*,/. &rotection, security less of an issue. *,
resource consumption becomes a big issue .computer only has 6789 of
memory/. *, bac$ to a shared subroutine library.
- 2 -
Hardware becomes cheaper and users more sophisticated. &eople need to
share data and information with other people. %omputers become more
information transfer, manipulation and storage devices rather than machines
that perform arithmetic operations. #etwor$ing becomes very important, and
as sharing becomes an important part of the experience so does security.
*perating systems become more sophisticated. ,tart putting bac$ features
present in the old time sharing systems .*,):, Windows #(, even 2nix/.
Rise of networ$. Internet is a huge popular phenomenon and drives new ways
of thin$ing about computing. *perating system is no longer interface to the
lower level machine ' people structure systems to contain layers of
middleware. ,o, a ;ava 1&I or something similar may be the primary thing
people need, not a set of system calls. In fact, what the operating system is
may become irrelevant as long as it supports the right set of middleware.
#etwor$ computer. %oncept of a box that gets all of its resources over the
networ$. #o local file system, +ust networ$ interfaces to ac5uire all outside
data. ,o have a slimmer version of *,.
In the future, computers will become physically small and portable. *perating
systems will have to deal with issues li$e disconnected operation and mobility.
&eople will also start using information with a psuedo'real time component
li$e voice and video. *perating systems will have to ad+ust to deliver
acceptable performance for these new forms of data.
What does a modern operating system do?
o Provides Abstrations Hardware has low'level physical resources
with complicated, idiosyncratic interfaces. *, provides abstractions
that present clean interfaces. !oal ma$e computer easier to use.
<xamples &rocesses, 2nbounded Memory, 0iles, ,ynchroni"ation and
%ommunication Mechanisms.
o Provides Standard Inter!ae !oal portability. 2nix runs on many
very different computer systems. (o a first approximation can port
programs across systems with little effort.
o "ediates Reso#re $sa%e !oal allow multiple users to share
resources fairly, efficiently, safely and securely. <xamples
Multiple processes share one processor. .preemptable resource/
Multiple programs share one physical memory .preemptable
resource/.
Multiple users and files share one dis$. .non'preemptable
resource/
Multiple programs share a given amount of dis$ and networ$
bandwidth .preemptable resource/.
o Cons#&es Reso#res ,olaris ta$es up about =Mbytes physical
memory .or about >788/.
1bstractions often wor$ well ' for example, timesharing, virtual memory and
hierarchical and networ$ed file systems. -ut, may brea$ down if stressed.
(imesharing gives poor performance if too many users run compute'intensive
+obs. ?irtual memory brea$s down if wor$ing set is too large .thrashing/, or if
- 3 -
there are too many large processes .machine runs out of swap space/.
1bstractions often fail for performance reasons.
1bstractions also fail because they prevent programmer from controlling
machine at desired level. <xample database systems often want to control
movement of information between dis$ and physical memory, and the paging
system can get in the way. More recently, existing *, schedulers fail to
ade5uately support multimedia and parallel processing needs, causing poor
performance.
%oncurrency and asynchrony ma$e operating systems very complicated
pieces of software. *perating systems are fundamentally non'deterministic
and event driven. %an be difficult to construct .hundreds of person'years of
effort/ and impossible to completely debug. <xamples of concurrency and
asynchrony
o I)* devices run concurrently with %&2, interrupting %&2 when done.
o *n a multiprocessor multiple user processes execute in parallel.
o Multiple wor$stations execute concurrently and communicate by
sending messages over a networ$. &rotocol processing ta$es place
asynchronously.
*perating systems are so large no one person understands whole system.
*utlives any of its original builders.
(he ma+or problem facing computer science today is how to build large,
reliable software systems. *perating systems are one of very few examples of
existing large software systems, and by studying operating systems we may
learn lessons applicable to the construction of larger systems.
PROCESS and THREA'S
1 process is an execution stream in the context of a particular process state.
o 1n execution stream is a se5uence of instructions.
o &rocess state determines the effect of the instructions. It usually
includes .but is not restricted to/
Registers
,tac$
Memory .global variables and dynamically allocated memory/
*pen file tables
,ignal management information
9ey concept processes are separated no process can directly affect
the state of another process.
&rocess is a $ey *, abstraction that users see ' the environment you interact
with when you use a computer is built up out of processes.
o (he shell you type stuff into is a process.
- 4 -
o When you execute a program you have +ust compiled, the *,
generates a process to run the program.
o @our WWW browser is a process.
*rgani"ing system activities around processes has proved to be a useful way
of separating out different activities into coherent units.
(wo concepts uniprogramming and multiprogramming.
o 2niprogramming only one process at a time. (ypical example 4*,.
&roblem users often wish to perform more than one activity at a time
.load a remote file while editing a program, for example/, and
uniprogramming does not allow this. ,o 4*, and other
uniprogrammed systems put in things li$e memory'resident programs
that invo$ed asynchronously, but still have separation problems. *ne
$ey problem with 4*, is that there is no memory protection ' one
program may write the memory of another program, causing weird
bugs.
o Multiprogramming multiple processes at a time. (ypical of 2nix plus
all currently envisioned new operating systems. 1llows system to
separate out activities cleanly.
Multiprogramming introduces the resource sharing problem ' which processes
get to use the physical resources of the machine when? *ne crucial resource
%&2. ,tandard solution is to use preemptive multitas$ing ' *, runs one
process for a while, then ta$es the %&2 away from that process and lets
another process run. Must save and restore process state. 9ey issue fairness.
Must ensure that all processes get their fair share of the %&2.
How does the *, implement the process abstraction? 2ses a context switch to
switch from running one process to running another process.
How does machine implement context switch? 1 processor has a limited
amount of physical resources. 0or example, it has only one register set. -ut
every process on the machine has its own set of registers. ,olution save and
restore hardware state on a context switch. ,ave the state in &rocess %ontrol
-loc$ .&%-/. What is in &%-? 4epends on the hardware.
o Registers ' almost all machines save registers in &%-.
o &rocessor ,tatus Word.
o What about memory? Most machines allow memory from multiple
processes to coexist in the physical memory of the machine. ,ome
may re5uire Memory Management 2nit .MM2/ changes on a context
switch. -ut, some early personal computers switched all of process3s
memory out to dis$ .AAA/.
*perating ,ystems are fundamentally event'driven systems ' they wait for an
event to happen, respond appropriately to the event, then wait for the next
event. <xamples
o 2ser hits a $ey. (he $eystro$e is echoed on the screen.
o 1 user program issues a system call to read a file. (he operating
system figures out which dis$ bloc$s to bring in, and generates a
re5uest to the dis$ controller to read the dis$ bloc$s into memory.
- 5 -
o (he dis$ controller finishes reading in the dis$ bloc$ and generates and
interrupt. (he *, moves the read data into the user program and
restarts the user program.
o 1 Mosaic or #etscape user as$s for a 2RB to be retrieved. (his
eventually generates re5uests to the *, to send re5uest pac$ets out
over the networ$ to a remote WWW server. (he *, sends the pac$ets.
o (he response pac$ets come bac$ from the WWW server, interrupting
the processor. (he *, figures out which process should get the
pac$ets, then routes the pac$ets to that process.
o (ime'slice timer goes off. (he *, must save the state of the current
process, choose another process to run, the give the %&2 to that
process.
When build an event'driven system with several distinct serial activities,
threads are a $ey structuring mechanism of the *,.
1 thread is again an execution stream in the context of a thread state. 9ey
difference between processes and threads is that multiple threads share parts
of their state. (ypically, allow multiple threads to read and write same
memory. .Recall that no processes could directly access memory of another
process/. -ut, each thread still has its own registers. 1lso has its own stac$,
but other threads can read and write the stac$ memory.
What is in a thread control bloc$? (ypically +ust registers. 4on3t need to do
anything to the MM2 when switch threads, because all threads can access
same memory.
(ypically, an *, will have a separate thread for each distinct activity. In
particular, the *, will have a separate thread for each process, and that
thread will perform *, activities on behalf of the process. In this case we say
that each user process is bac$ed by a $ernel thread.
o When process issues a system call to read a file, the process3s thread
will ta$e over, figure out which dis$ accesses to generate, and issue
the low level instructions re5uired to start the transfer. It then
suspends until the dis$ finishes reading in the data.
o When process starts up a remote (%& connection, its thread handles
the low'level details of sending out networ$ pac$ets.
Having a separate thread for each activity allows the programmer to program
the actions associated with that activity as a single serial stream of actions
and events. &rogrammer does not have to deal with the complexity of
interleaving multiple activities on the same thread.
Why allow threads to access same memory? -ecause inside *,, threads must
coordinate their activities very closely.
o If two processes issue read file system calls at close to the same time,
must ma$e sure that the *, seriali"es the dis$ re5uests appropriately.
o When one process allocates memory, its thread must find some free
memory and give it to the process. Must ensure that multiple threads
allocate dis+oint pieces of memory.
- 6 -
Having threads share the same address space ma$es it much easier to
coordinate activities ' can build data structures that represent system state
and have threads read and write data structures to figure out what to do
when they need to process a re5uest.
*ne complication that threads must deal with asynchrony. 1synchronous
events happen arbitrarily as the thread is executing, and may interfere with
the thread3s activities unless the programmer does something to limit the
asynchrony. <xamples
o 1n interrupt occurs, transferring control away from one thread to an
interrupt handler.
o 1 time'slice switch occurs, transferring control from one thread to
another.
o (wo threads running on different processors read and write the same
memory.
1synchronous events, if not properly controlled, can lead to incorrect
behavior. <xamples
o (wo threads need to issue dis$ re5uests. 0irst thread starts to program
dis$ controller .assume it is memory'mapped, and must issue multiple
writes to specify a dis$ operation/. In the meantime, the second
thread runs on a different processor and also issues the memory'
mapped writes to program the dis$ controller. (he dis$ controller gets
horribly confused and reads the wrong dis$ bloc$.
o (wo threads need to write to the display. (he first thread starts to
build its re5uest, but before it finishes a time'slice switch occurs and
the second thread starts its re5uest. (he combination of the two
threads issues a forbidden re5uest se5uence, and smo$e starts
pouring out of the display.
o 0or accounting reasons the operating system $eeps trac$ of how much
time is spent in each user program. It also $eeps a running sum of the
total amount of time spent in all user programs. (wo threads
increment their local counters for their processes, then concurrently
increment the global counter. (heir increments interfere, and the
recorded total time spent in all user processes is less than the sum of
the local times.
,o, programmers need to coordinate the activities of the multiple threads so
that these bad things don3t happen. 9ey mechanism synchroni"ation
operations. (hese operations allow threads to control the timing of their
events relative to events in other threads. 1ppropriate use allows
programmers to avoid problems li$e the ones outlined above.
Thread Creation, "anip#lation and S(nhroni)ation
We first must postulate a thread creation and manipulation interface. Will use
the one in #achos
- 7 -
class Thread {
public:
Thread(char* debugName);
~Thread();
void Fork(void (*func)(int) int arg);
void !ield();
void Finish();
"
(he Thread constructor creates a new thread. It allocates a data structure
with space for the (%-.
(o actually start the thread running, must tell it what function to start running
when it runs. (he Fork method gives it the function and a parameter to the
function.
What does Fork do? It first allocates a stac$ for the thread. It then sets up
the (%- so that when the thread starts running, it will invo$e the function and
pass it the correct parameter. It then puts the thread on a run 5ueue
someplace. Fork then returns, and the thread that called Fork continues.
How does *, set up (%- so that the thread starts running at the function?
0irst, it sets the stac$ pointer in the (%- to the stac$. (hen, it sets the &% in
the (%- to be the first instruction in the function. (hen, it sets the register in
the (%- holding the first parameter to the parameter. When the thread
system restores the state from the (%-, the function will magically start to
run.
(he system maintains a 5ueue of runnable threads. Whenever a processor
becomes idle, the thread scheduler grabs a thread off of the run 5ueue and
runs the thread.
%onceptually, threads execute concurrently. (his is the best way to reason
about the behavior of threads. -ut in practice, the *, only has a finite
number of processors, and it can3t run all of the runnable threads at once. ,o,
must multiplex the runnable threads on the finite number of processors.
Bet3s do a few thread examples. 0irst example two threads that increment a
variable.
int a # $;
void sum(int p) {
a%%;
printf(&'d : a # 'd(n& p a);
"
void main() {
Thread *t # ne) Thread(&child&);
t*+Fork(sum ,);
sum($);
"
(he two calls to sum run concurrently. What are the possible results of the
program? (o understand this fully, we must brea$ the sum subroutine up into
its primitive components.
- 8 -
sum first reads the value of a into a register. It then increments the register,
then stores the contents of the register bac$ into a. It then reads the values
of of the control string, p and a into the registers that it uses to pass
arguments to the printf routine. It then calls printf, which prints out the
data.
(he best way to understand the instruction se5uence is to loo$ at the
generated assembly language .cleaned up +ust a bit/. @ou can have the
compiler generate assembly code instead of ob+ect code by giving it the ',
flag. It will put the generated assembly in the same file name as the .c or .cc
file, but with a .s suffix.
la a 'r$
ld -'r$.'r,
add 'r,,'r,
st 'r,-'r$.

ld -'r$. 'o/ 0 parameters are passed starting )ith


'o$
mov 'o$ 'o,
la 12,3 'o$
call printf
,o when execute concurrently, the result depends on how the instructions
interleave. What are possible results?
$ : , $ : ,
, : 4 , : ,

, : 4 , : ,
$ : , $ : ,

, : , $ : 4
$ : 4 , : 4

$ : 4 , : 4
, : , $ : 4
,o the results are nondeterministic ' you may get different results when you
run the program more than once. ,o, it can be very difficult to reproduce
bugs. #ondeterministic execution is one of the things that ma$es writing
parallel programs much more difficult than writing serial programs.
%hances are, the programmer is not happy with all of the possible results
listed above. &robably wanted the value of a to be : after both threads finish.
(o achieve this, must ma$e the increment operation atomic. (hat is, must
prevent the interleaving of the instructions in a way that would interfere with
the additions.
- 9 -
%oncept of atomic operation. 1n atomic operation is one that executes
without any interference from other operations ' in other words, it executes
as one unit. (ypically build complex atomic operations up out of se5uences of
primitive operations. In our case the primitive operations are the individual
machine instructions.
More formally, if several atomic operations execute, the final result is
guaranteed to be the same as if the operations executed in some serial order.
In our case above, build an increment operation up out of loads, stores and
add machine instructions. Want the increment operation to be atomic.
2se synchroni"ation operations to ma$e code se5uences atomic. 0irst
synchroni"ation abstraction semaphores. 1 semaphore is, conceptually, a
counter that supports two atomic operations, & and ?. Here is the ,emaphore
interface from #achos
class 5emaphore {
public:
5emaphore(char* debugName int initial6alue);
~5emaphore();
void 7();
void 6();
"
Here is what the operations do
o ,emphore.name, count/ creates a semaphore and initiali"es the
counter to count.
o &./ 1tomically waits until the counter is greater than 8, then
decrements the counter and returns.
o ?./ 1tomically increments the counter.
Here is how we can use the semaphore to ma$e the sum example wor$
int a # $;
5emaphore *s;
void sum(int p) {
int t;
s*+7();
a%%;
t # a;
s*+6();
printf(&'d : a # 'd(n& p t);
"
void main() {
Thread *t # ne) Thread(&child&);
s # ne) 5emaphore(&s& ,);
t*+Fork(sum ,);
sum($);
"
We are using semaphores here to implement a mutual exclusion mechanism.
(he idea behind mutual exclusion is that only one thread at a time should be
- 10 -
allowed to do something. In this case, only one thread should access a. 2se
mutual exclusion to ma$e operations atomic. (he code that performs the
atomic operation is called a critical section.
,emaphores do much more than mutual exclusion. (hey can also be used to
synchroni"e producer)consumer programs. (he idea is that the producer is
generating data and the consumer is consuming data. ,o a 2nix pipe has a
producer and a consumer. @ou can also thin$ of a person typing at a $eyboard
as a producer and the shell program reading the characters as a consumer.
Here is the synchroni"ation problem ma$e sure that the consumer does not
get ahead of the producer. -ut, we would li$e the producer to be able to
produce without waiting for the consumer to consume. %an use semaphores
to do this. Here is how it wor$s
5emaphore *s;
void consumer(int dumm8) {
)hile (,) {
s*+7();
consume the ne9t unit of data
"
"
void producer(int dumm8) {
)hile (,) {
produce the ne9t unit of data
s*+6();
"
"
void main() {
s # ne) 5emaphore(&s& $);
Thread *t # ne) Thread(&consumer&);
t*+Fork(consumer ,);
t # ne) Thread(&producer&);
t*+Fork(producer ,);
"
In some sense the semaphore is an abstraction of the collection of data.
In the real world, pragmatics intrude. If we let the producer run forever and
never run the consumer, we have to store all of the produced data
somewhere. -ut no machine has an infinite amount of storage. ,o, we want
to let the producer to get ahead of the consumer if it can, but only a given
amount ahead. We need to implement a bounded buffer which can hold only
# items. If the bounded buffer is full, the producer must wait before it can put
any more data in.
5emaphore *full;
5emaphore *empt8;
void consumer(int dumm8) {
- 11 -
)hile (,) {
full*+7();
consume the ne9t unit of data
empt8*+6();
"
"
void producer(int dumm8) {
)hile (,) {
empt8*+7();
produce the ne9t unit of data
full*+6();
"
"
void main() {
empt8 # ne) 5emaphore(&empt8& N);
full # ne) 5emaphore(&full& $);
Thread *t # ne) Thread(&consumer&);
t*+Fork(consumer ,);
t # ne) Thread(&producer&);
t*+Fork(producer ,);
"
1n example of where you might use a producer and consumer in an operating
system is the console .a device that reads and writes characters from and to
the system console/. @ou would probably use semaphores to ma$e sure you
don3t try to read a character before it is typed.
,emaphores are one synchroni"ation abstraction. (here is another called
loc$s and condition variables.
Boc$s are an abstraction specifically for mutual exclusion only. Here is the
#achos loc$ interface
class 2ock {
public:
2ock(char* debugName); :: initiali;e lock to be
F<==
~2ock(); :: deallocate lock
void >c?uire(); :: these are the onl8 operations on a lock
void <elease(); :: the8 are both *atomic*
"
1 loc$ can be in one of two states loc$ed and unloc$ed. ,emantics of loc$
operations
o Boc$.name/ creates a loc$ that starts out in the unloc$ed state.
o 1c5uire./ 1tomically waits until the loc$ state is unloc$ed, then sets
the loc$ state to loc$ed.
o Release./ 1tomically changes the loc$ state to unloc$ed from loc$ed.
- 12 -
In assignment C you will implement loc$s in #achos on top of semaphores.
What are re5uirements for a loc$ing implementation?
o *nly one thread can ac5uire loc$ at a time. .safety/
o If multiple threads try to ac5uire an unloc$ed loc$, one of the threads
will get it. .liveness/
o 1ll unloc$s complete in finite time. .liveness/
What are desirable properties for a loc$ing implementation?
o <fficiency ta$e up as little resources as possible.
o 0airness threads ac5uire loc$ in the order they as$ for it. 1re also
wea$er forms of fairness.
o ,imple to use.
When use loc$s, typically associate a loc$ with pieces of data that multiple
threads access. When one thread wants to access a piece of data, it first
ac5uires the loc$. It then performs the access, then unloc$s the loc$. ,o, the
loc$ allows threads to perform complicated atomic operations on each piece of
data.
%an you implement unbounded buffer only using loc$s? (here is a problem ' if
the consumer wants to consume a piece of data before the producer produces
the data, it must wait. -ut loc$s do not allow the consumer to wait until the
producer produces the data. ,o, consumer must loop until the data is ready.
(his is bad because it wastes %&2 resources.
(here is another synchroni"ation abstraction called condition variables +ust for
this $ind of situation. Here is the #achos interface
class @ondition {
public:
@ondition(char* debugName);
~@ondition();
void Aait(2ock *condition2ock);
void 5ignal(2ock *condition2ock);
void Broadcast(2ock *condition2ock);
"
,emantics of condition variable operations
o %ondition.name/ creates a condition variable.
o Wait.Boc$ Dl/ 1tomically releases the loc$ and waits. When Wait
returns the loc$ will have been reac5uired.
o ,ignal.Boc$ Dl/ <nables one of the waiting threads to run. When
,ignal returns the loc$ is still ac5uired.
o -roadcast.Boc$ Dl/ <nables all of the waiting threads to run. When
-roadcast returns the loc$ is still ac5uired.
1ll loc$s must be the same. In assignment C you will implement condition
variables in #achos on top of semaphores.
- 13 -
(ypically, you associate a loc$ and a condition variable with a data structure.
-efore the program performs an operation on the data structure, it ac5uires
the loc$. If it has to wait before it can perform the operation, it uses the
condition variable to wait for another operation to bring the data structure
into a state where it can perform the operation. In some cases you need more
than one condition variable.
Bet3s say that we want to implement an unbounded buffer using loc$s and
condition variables. In this case we have : consumers.
2ock *l;
@ondition *c;
int avail # $;
void consumer(int dumm8) {
)hile (,) {
l*+>c?uire();
if (avail ## $) {
c*+Aait(l);
"
consume the ne9t unit of data
avail**;
l*+<elease();
"
"
void producer(int dumm8) {
)hile (,) {
l*+>c?uire();
produce the ne9t unit of data
avail%%;
c*+5ignal(l);
l*+<elease();
"
"
void main() {
l # ne) 2ock(&l&);
c # ne) @ondition(&c&);
Thread *t # ne) Thread(&consumer&);
t*+Fork(consumer ,);
Thread *t # ne) Thread(&consumer&);
t*+Fork(consumer 4);
t # ne) Thread(&producer&);
t*+Fork(producer ,);
"
(here are two variants of condition variables Hoare condition variables and
Mesa condition variables. 0or Hoare condition variables, when one thread
performs a 5ignal, the very next thread to run is the waiting thread. 0or
Mesa condition variables, there are no guarantees when the signalled thread
- 14 -
will run. *ther threads that ac5uire the loc$ can execute between the
signaller and the waiter. (he example above will wor$ with Hoare condition
variables but not with Mesa condition variables.
What is the problem with Mesa condition variables? %onsider the following
scenario (hree threads, thread C one producing data, threads : and E
consuming data.
o (hread : calls consumer, and suspends.
o (hread C calls producer, and signals thread :.
o Instead of thread : running next, thread E runs next, calls consumer,
and consumes the element. .#ote with Hoare monitors, thread :
would always run next, so this would not happen./
o (hread : runs, and tries to consume an item that is not there.
4epending on the data structure used to store produced items, may
get some $ind of illegal access error.
How can we fix this problem? Replace the if with a )hile.
void consumer(int dumm8) {
)hile (,) {
l*+>c?uire();
)hile (avail ## $) {
c*+Aait(l);
"
consume the ne9t unit of data
avail**;
l*+<elease();
"
"
In general, this is a crucial point. 1lways put )hile3s around your condition
variable code. If you don3t, you can get really obscure bugs that show up very
infre5uently.
In this example, what is the data that the loc$ and condition variable are
associated with? (he avail variable.
&eople have developed a programming abstraction that automatically
associates loc$s and condition variables with data. (his abstraction is called a
monitor. 1 monitor is a data structure plus a set of operations .sort of li$e an
abstract data type/. (he monitor also has a loc$ and, optionally, one or more
condition variables. ,ee notes for Becture C7.
(he compiler for the monitor language automatically inserts a loc$ operation
at the beginning of each routine and an unloc$ operation at the end of the
routine. ,o, programmer does not have to put in the loc$ operations.
Monitor languages were popular in the middle =83s ' they are in some sense
safer because they eliminate one possible programming error. -ut more
recent languages have tended not to support monitors explicitly, and expose
the loc$ing operations to the programmer. ,o the programmer has to insert
- 15 -
the loc$ and unloc$ operations by hand. ;ava ta$es a middle ground ' it
supports monitors, but also allows programmers to exert finer grain control
over the loc$ed sections by supporting synchroni"ed bloc$s within methods.
-ut synchroni"ed bloc$s still present a structured model of synchroni"ation,
so it is not possible to mismatch the loc$ ac5uire and release.
Baundromat <xample 1 local laudromat has switched to a computeri"ed
machine allocation scheme. (here are # machines, numbered C to #. -y the
front door there are & allocation stations. When you want to wash your
clothes, you go to an allocation station and put in your coins. (he allocation
station gives you a number, and you use that machine. (here are also &
deallocation stations. When your clothes finish, you give the number bac$ to
one of the deallocation stations, and someone else can use the machine. Here
is the alpha release of the machine allocation software
allocate(int dumm8) {
)hile (,) {
)ait for coins from user
n # get();
give number n to user
"
"
deallocate(int dumm8) {
)hile (,) {
)ait for number n from user
put(i);
"
"
main() {
for (i # $; i C 7; i%%) {
t # ne) Thread(&allocate&);
t*+Fork(allocate $);
t # ne) Thread(&deallocate&);
t*+Fork(deallocate $);
"
"
(he $ey parts of the scheduling are done in the two routines get and put,
which use an array data structure a to $eep trac$ of which machines are in
use and which are free.
int a-N.;
int get() {
for (i # $; i C N; i%%) {
if (a-i. ## $) {
a-i. # ,;
return(i%,);
"
"
- 16 -
"
void put(int i) {
a-i*,. # $;
"
It seems that the alpha software isn3t doing all that well. ;ust loo$ing at the
software, you can see that there are several synchroni"ation problems.
(he first problem is that sometimes two people are assigned to the same
machine. Why does this happen? We can fix this with a loc$
int a-N.;
2ock *l;
int get() {
l*+>c?uire();
for (i # $; i C N; i%%) {
if (a-i. ## $) {
a-i. # ,;
l*+<elease();
return(i%,);
"
"
l*+<elease();
"
void put(int i) {
l*+>c?uire();
a-i*,. # $;
l*+<elease();
"
,o now, have fixed the multiple assignment problem. -ut what happens if
someone comes in to the laundry when all of the machines are already ta$en?
What does the machine return? Must fix it so that the system waits until there
is a machine free before it returns a number. (he situation calls for condition
variables.
int a-N.;
2ock *l;
@ondition *c;
int get() {
l*+>c?uire();
)hile (,) {
for (i # $; i C N; i%%) {
if (a-i. ## $) {
a-i. # ,;
l*+<elease();
return(i%,);
"
- 17 -
"
c*+Aait(l);
"
"
void put(int i) {
l*+>c?uire();
a-i*,. # $;
c*+5ignal();
l*+<elease();
"
What data is the loc$ protecting? (he a array.
When would you use a broadcast operation? Whenever want to wa$e up all
waiting threads, not +ust one. 0or an event that happens only once. 0or
example, a bunch of threads may wait until a file is deleted. (he thread that
actually deleted the file could use a broadcast to wa$e up all of the threads.
1lso use a broadcast for allocation)deallocation of variable si"ed units.
<xample concurrent malloc)free.
2ock *l;
@ondition *c;
char *malloc(int s) {
l*+>c?uire();
)hile (cannot allocate a chunk of si;e s) {
c*+Aait(l);
"
allocate chunk of si;e s;
l*+<elease();
return pointer to allocated chunk
"
void free(char *m) {
l*+>c?uire();
deallocate m1
c*+Broadcast(l);
l*+<elease();
"
<xample with malloc)free. Initially start out with C8 bytes free.
(ime &rocess C &rocess : &rocess E
malloc.C8/ ' succeeds malloc.F/ ' suspends loc$ malloc.F/ suspends loc$
C gets loc$ ' waits
: gets loc$ ' waits
E free.C8/ ' broadcast
7 resume malloc.F/ ' succeeds
- 18 -
F resume malloc.F/ ' succeeds
6 malloc.G/ ' waits
G malloc.E/ ' waits
= free.F/ ' broadcast
H resume malloc.G/ ' waits
C8 resume malloc.E/ ' succeeds
What would happen if changed c*+Broadcast(l) to c*+5ignal(l)? 1t step
C8, process E would not wa$e up, and it would not get the chance to allocate
available memory. What would happen if changed )hile loop to an if?
@ou will be as$ed to implement condition variables as part of assignment C.
(he following implementation is I#%*RR<%(. &lease do not turn this
implementation in.
class @ondition {
private:
int )aiting;
5emaphore *sema;
"
void @ondition::Aait(2ock* l)
{
)aiting%%;
l*+<elease();
sema*+7();
l*+>c?uire();
"
void @ondition::5ignal(2ock* l)
{
if ()aiting + $) {
sema*+6();
)aiting**;
"
"
Why is this solution incorrect? -ecause in some cases the signalling thread
may wa$e up a waiting thread that called Wait after the signalling thread
called ,ignal.
- 19 -

Vous aimerez peut-être aussi