Title: Chapter 6: Process Synchronization
1Chapter 6 Process Synchronization
2Module 6 Process Synchronization
- Background
- The Critical-Section Problem
- Petersons Solution
- Synchronization Hardware
- Semaphores
- Classic Problems of Synchronization
- Monitors
- Synchronization Examples
- Atomic Transactions
3Background
- Concurrent access to shared data may result in
data inconsistency - Maintaining data consistency requires mechanisms
to ensure the orderly execution of cooperating
processes - Suppose that we wanted to provide a solution to
the consumer-producer problem that fills all the
buffers. - Use an integer count that keeps track of the
number of full buffers. - Initially, count is set to 0.
- It is incremented by the producer after it
produces a new buffer and - is decremented by the consumer after it consumes
a buffer.
4Producer
- while (true)
-
- / produce an item
and put in nextProduced - while (count BUFFER_SIZE)
- // do nothing
- buffer in nextProduced
- in (in 1) BUFFER_SIZE
- count
-
5Consumer
- while (1)
-
- while (count 0)
- // do nothing
- nextConsumed bufferout
- out (out 1) BUFFER_SIZE
- count--
- / consume the item in nextConsumed
-
6Race Condition
- count could be implemented as register1
count register1 register1 1 count
register1 - count-- could be implemented as register2
count register2 register2 - 1 count
register2 - Consider this execution interleaving with count
5 initially - S0 producer execute register1 count
register1 5S1 producer execute register1
register1 1 register1 6 S2 consumer
execute register2 count register2 5 S3
consumer execute register2 register2 - 1
register2 4 S4 producer execute count
register1 count 6 S5 consumer execute
count register2 count 4
7Solution to Critical-Section Problem
- 1. Mutual Exclusion - If process Pi is executing
in its critical section, then no other processes
can be executing in their critical sections - 2. Progress - If no process is executing in its
critical section and there exist some processes
that wish to enter their critical section, then
the selection of the processes that will enter
the critical section next cannot be postponed
indefinitely - 3. Bounded Waiting - A bound must exist on the
number of times that other processes are allowed
to enter their critical sections after a process
has made a request to enter its critical section
and before that request is granted - Assume that each process executes at a nonzero
speed - No assumption concerning relative speed of the N
processes
8Petersons Solution
- Two process solution
- Assume that the LOAD and STORE instructions are
atomic that is, cannot be interrupted. - The two processes share two variables
- int turn
- Boolean flag2
- The variable turn indicates whose turn it is to
enter the critical section. - The flag array is used to indicate if a process
is ready to enter the critical section. flagi
true implies that process Pi is ready!
9Algorithm for Process Pi
- do
- flagi TRUE
- turn j
- while ( flagj turn j)
- CRITICAL SECTION
- flagi FALSE
- REMAINDER SECTION
- while (TRUE)
-
10Synchronization Hardware
- Many systems provide hardware support for
critical section code - Uniprocessors could disable interrupts
- Currently running code would execute without
preemption - Generally too inefficient on multiprocessor
systems - Operating systems using this not broadly scalable
- Modern machines provide special atomic hardware
instructions - Atomic non-interruptable
- Either test memory word and set value
- Or swap contents of two memory words
11TestAndndSet Instruction
- Definition
- boolean TestAndSet (boolean target)
-
- boolean rv target
- target TRUE
- return rv
-
12Implementing SemaphoresTest and Set Instruction
- TS R3, m test-and-set of location m
- TS(m) Reg_i memorym memorym TRUE CC
value(Reg_i)
Data Register
ConditionCode Register
R3
m
FALSE
Primary Memory
13Solution using TestAndSet
- Shared boolean variable lock., initialized to
false. - Solution
- do
- while ( TestAndSet (lock ))
- / do nothing
- // critical section
- lock FALSE
- // remainder section
- while ( TRUE)
-
14Swap Instruction
- Definition
- void Swap (boolean a, boolean b)
-
- boolean temp a
- a b
- b temp
-
15Solution using Swap
- Shared Boolean variable lock initialized to
FALSE Each process has a local Boolean variable
key. - Solution
- do
- key TRUE
- while ( key TRUE)
- Swap (lock, key )
-
- // critical section
- lock FALSE
- // remainder section
- while ( TRUE)
-
16Semaphore
- Synchronization tool that does not require busy
waiting - Implemented by the OS
- Implemented without busy waiting
- Semaphore S integer variable
- Two standard operations modify S wait() and
signal() - Originally called P() and V()
- P(s) proberen, to test
- V(s) verhogen, to increment or signal
- Less complicated
17Semaphore Implementation with no Busy waiting
(Cont.)
- Implementation of wait with no busy waiting
- wait (S)
- value--
- if (value lt 0)
- add this process to waiting
queue - block()
-
- Implementation of signal
- Signal (S)
- value
- if (value lt 0)
- remove a process P from the
waiting queue - wakeup(P)
-
18Semaphore as General Synchronization Tool
- Counting semaphore integer value can range over
an unrestricted domain - Binary semaphore integer value can range only
between 0 and 1 can be simpler to implement - Also known as mutex locks
- Can implement a counting semaphore S as a binary
semaphore
19Using the TS Instruction
Solution using TS
Solution using semaphores
boolean s FALSE . . . while(TS(s))
ltcritical sectiongt s FALSE . . .
semaphore s 1 . . . wait(s) ltcritical
sectiongt signal(s) . . .
The first process finds s to be FALSE. All
remaining process find s to be TRUE until the
first process executes s FALSE
20Using the TS Instruction
- The TS instruction can be used to implement the
binary semaphore. - Can it be used for the general semaphore?
21Semaphore Implementation
- Must guarantee that no two processes can execute
wait () and signal () on the same semaphore at
the same time - Thus, implementation becomes the critical section
problem where the wait and signal code are placed
in the critical section. - Could now have busy waiting in critical section
implementation - But implementation code is short
- Little busy waiting if critical section rarely
occupied - Note that applications may spend lots of time in
critical sections and therefore this is not a
good solution. -
22Semaphore Implementation with no Busy waiting
- With each semaphore there is an associated
waiting queue. Each entry in a waiting queue has
two data items - value (of type integer)
- pointer to next record in the list
- Two operations
- block place the process invoking the operation
on the appropriate waiting queue. - wakeup remove one of processes in the waiting
queue and place it in the ready queue. -
23Implementing the General Semaphore
struct semaphore int value ltinitial
valuegt boolean mutex FALSE boolean hold
TRUE shared struct semaphore s wait(struct
semaphore s) while(TS(s.mutex))
s.value-- if(s.value lt 0) ( s.mutex
FALSE while(TS(s.hold)) else
s.mutex FALSE
Variable mutex ensures mutual exclusion in the CS.
This while statement is necessary to prevent
a race . The signaling process loops
until another process does a P operation.
signal(struct semaphore s) while(TS(s.mutex))
s.value if(s.value lt 0) (
while(!s.hold) s.hold FALSE
s.mutex FALSE
When a process signals, the value is incremented.
Variable hold causes processes to busy-wait if
the semaphore is less than 0.
24Implementing the General Semaphore
Solution without busy-waiting
struct semaphore int value ltinitial
valuegt boolean mutex FALSE boolean hold
TRUE shared struct semaphore s wait(struct
semaphore s) while(TS(s.mutex)) yield (,
scheduler) s.value-- if(s.value lt 0) (
s.mutex FALSE while(TS(s.hold)) yield (,
scheduler) else s.mutex FALSE
When a process must wait, it yields the CPU. Uses
the yield instruction from chapter 7.
signal(struct semaphore s) while(TS(s.mutex))
yield (, scheduler) s.value
if(s.value lt 0) ( while(!s.hold) yield (,
scheduler) s.hold FALSE s.mutex
FALSE
25Active vs Passive Semaphores
- A process can dominate the semaphore
- Performs signal operation, but continues to
execute - Performs another wait operation before releasing
the CPU - Called a passive implementation of V
- Active implementation calls scheduler as part of
the V operation. - Changes semantics of semaphore!
- Cause people to rethink solutions
26Deadlock and Starvation
- Deadlock two or more processes are waiting
indefinitely for an event that can be caused by
only one of the waiting processes - Let S and Q be two semaphores initialized to 1
- P0 P1
- wait (S)
wait (Q) - wait (Q)
wait (S) - . .
- . .
- . .
- signal (S)
signal (Q) - signal (Q)
signal (S) - Starvation indefinite blocking. A process may
never be removed from the semaphore queue in
which it is suspended.
27Classical Problems of Synchronization
- Bounded-Buffer Problem
- Readers and Writers Problem
- Dining-Philosophers Problem
28Bounded Buffer Problem
Empty Pool
Producer
Consumer
Full Pool
29Bounded-Buffer Problem
- use counting semaphores
- they keep a count of the number of empty and full
buffers - also synchronize the producer/consumer
- empty semaphore used to block producer if no
empty buffers left - initialized to the value N.
- full semaphore used to block consumer if no full
buffers left - initialized to the value 0
- mutex semaphore protects CS where buffers are
manipulated N buffers, each can hold one item - initialized to the value 1
30Bounded Buffer Problem (Cont.)
- The structure of the producer process
- do
- // produce an item
- wait (empty)
- wait (mutex)
- // add the item to the
buffer - signal (mutex)
- signal (full)
- while (true)
31Bounded Buffer Problem (Cont.)
- The structure of the consumer process
- do
- wait (full)
- wait (mutex)
- // remove an item from
buffer - signal (mutex)
- signal (empty)
-
- // consume the removed item
- while (true)
32Bounded Buffer Problem (Unix)
producer() buf_type next, here
while(TRUE) produce_item(next) / Claim
an empty / P(empty) P(mutex)
here obtain(empty) V(mutex)
copy_buffer(next, here) P(mutex)
release(here, fullPool) V(mutex) /
Signal a full buffer / V(full)
semaphore mutex 1 semaphore full 0
/ A general (counting) semaphore / semaphore
empty N / A general (counting) semaphore
/ buf_type bufferN fork(producer,
0) fork(consumer, 0)
consumer() buf_type next, here
while(TRUE) / Claim full buffer /
P(mutex) P(full) here
obtain(full) V(mutex) copy_buffer(here,
next) P(mutex) release(here,
emptyPool) V(mutex) / Signal an empty
buffer / V(empty) consume_item(next)
33Bounded Buffer Problem (Unix)
producer() buf_type next, here
while(TRUE) produce_item(next) / Claim
an empty / P(empty) P(mutex)
here obtain(empty) V(mutex)
copy_buffer(next, here) P(mutex)
release(here, fullPool) V(mutex) /
Signal a full buffer / V(full)
semaphore mutex 1 semaphore full 0
/ A general (counting) semaphore / semaphore
empty N / A general (counting) semaphore
/ buf_type bufferN fork(producer,
0) fork(consumer, 0)
consumer() buf_type next, here
while(TRUE) / Claim full buffer /
P(full) P(mutex) here
obtain(full) V(mutex) copy_buffer(here,
next) P(mutex) release(here,
emptyPool) V(mutex) / Signal an empty
buffer / V(empty) consume_item(next)
34Readers-Writers Problem
- A data set is shared among a number of concurrent
processes - Readers only read the data set they do not
perform any updates - Writers can both read and write.
- Problem allow multiple readers to read at the
same time. Only one single writer can access the
shared data at the same time. - Shared Data
- Data set
- Semaphore mutex initialized to 1.
- Semaphore wrt initialized to 1.
- Integer readcount initialized to 0.
35Readers-Writers Problem (Cont.)
- The structure of a writer process
-
- do
- wait (wrt)
-
- // writing is performed
- signal (wrt)
- while (true)
-
36Readers-Writers Problem (Cont.)
- The structure of a reader process
-
- do
- wait (mutex)
- readcount
- if (readercount 1) wait
(wrt) - signal (mutex)
-
- // reading is
performed - wait (mutex)
- readcount - -
- if redacount 0) signal
(wrt) - signal (mutex)
- while (true)
-
37First Unix Solution
reader() while(TRUE) ltother
computinggt P(mutex) readCount
if(readCount 1) P(writeBlock)
V(mutex) / Critical section /
access(resource) P(mutex)
readCount-- if(readCount 0)
V(writeBlock) V(mutex) resourceType
resource int readCount 0 semaphore mutex
1 semaphore writeBlock 1 fork(reader,
0) fork(writer, 0)
writer() while(TRUE) ltother
computinggt P(writeBlock) / Critical
section / access(resource)
V(writeBlock)
38First Solution (2)
reader() while(TRUE) ltother
computinggt P(mutex) readCount
if(readCount 1) P(writeBlock)
V(mutex) / Critical section /
access(resource) P(mutex)
readCount-- if(readCount 0)
V(writeBlock) V(mutex) resourceType
resource int readCount 0 semaphore mutex
1 semaphore writeBlock 1 fork(reader,
0) fork(writer, 0)
writer() while(TRUE) ltother
computinggt P(writeBlock) / Critical
section / access(resource)
V(writeBlock)
- First reader competes with writers
- Last reader signals writers
- Any writer must wait for all readers
- Readers can starve writers
- Updates can be delayed forever
- May not be what we want
39Writer Precedence (Unix)
reader() while(TRUE) ltother
computinggt P(readBlock)
P(mutex1) readCount
if(readCount 1) P(writeBlock)
V(mutex1) V(readBlock)
access(resource) P(mutex1)
readCount-- if(readCount 0)
V(writeBlock) V(mutex1) int readCount
0, writeCount 0 semaphore mutex 1, mutex2
1 semaphore readBlock 1, writeBlock 1,
writePending 1 fork(reader, 0) fork(writer,
0)
writer() while(TRUE) ltother
computinggt P(mutex2) writeCount
if(writeCount 1) P(readBlock)
V(mutex2) P(writeBlock)
access(resource) V(writeBlock)
P(mutex2) writeCount-- if(writeCount
0) V(readBlock) V(mutex2)
First Writer does a P(readBlock) to block any new
readers
40Writer Precedence (in Unix)
reader() while(TRUE) ltother
computinggt P(readBlock)
P(mutex1) readCount
if(readCount 1) P(writeBlock)
V(mutex1) V(readBlock)
access(resource) P(mutex1)
readCount-- if(readCount 0)
V(writeBlock) V(mutex1) int readCount
0, writeCount 0 semaphore mutex 1, mutex2
1 semaphore readBlock 1, writeBlock 1,
writePending 1 fork(reader, 0) fork(writer,
0)
writer() while(TRUE) ltother
computinggt P(mutex2) writeCount
if(writeCount 1) P(readBlock)
V(mutex2) P(writeBlock)
access(resource) V(writeBlock)
P(mutex2) writeCount-- if(writeCount
0) V(readBlock) V(mutex2)
Next reader is blocked because the first
writer did a P(readBlock)
Writer blocks on P(writeBlock)
41Writer Precedence (in Unix)
reader() while(TRUE) ltother
computinggt P(readBlock)
P(mutex1) readCount
if(readCount 1) P(writeBlock)
V(mutex1) V(readBlock)
access(resource) P(mutex1)
readCount-- if(readCount 0)
V(writeBlock) V(mutex1) int readCount
0, writeCount 0 semaphore mutex 1, mutex2
1 semaphore readBlock 1, writeBlock 1,
writePending 1 fork(reader, 0) fork(writer,
0)
writer() while(TRUE) ltother
computinggt P(mutex2) writeCount
if(writeCount 1) P(readBlock)
V(mutex2) P(writeBlock)
access(resource) V(writeBlock)
P(mutex2) writeCount-- if(writeCount
0) V(readBlock) V(mutex2)
42Writer Precedence (2)
reader() while(TRUE) ltother
computinggt P(writePending)
P(readBlock) P(mutex1)
readCount if(readCount 1)
P(writeBlock) V(mutex1)
V(readBlock) V(writePending)
access(resource) P(mutex1)
readCount-- if(readCount 0)
V(writeBlock) V(mutex1) int readCount
0, writeCount 0 semaphore mutex 1, mutex2
1 semaphore readBlock 1, writeBlock 1,
writePending 1 fork(reader, 0) fork(writer,
0)
writer() while(TRUE) ltother
computinggt P(mutex2) writeCount
if(writeCount 1) P(readBlock)
V(mutex2) P(writeBlock)
access(resource) V(writeBlock)
P(mutex2) writeCount-- if(writeCount
0) V(readBlock) V(mutex2)
Any new reader must wait until the last
writer signals
Any new writer has priority over any waiting
reader but is stuck until first writer is
finished
43Writer Precedence (2)
- Any writer must wait for current readers to
finish - Any writer has priority over any new readers
- The writers can starve readers
- Reads can be delayed forever
- May not be what we want
44Dining-Philosophers Problem
- Shared data
- Bowl of rice (data set)
- Semaphore chopstick 5 initialized to 1
45Dining-Philosophers Problem (Cont.)
- The structure of Philosopher i
- Do
- wait ( chopsticki )
- wait ( chopStick (i 1) 5 )
-
- // eat
- signal ( chopsticki )
- signal (chopstick (i 1) 5 )
-
- // think
- while (true)
Deadlock! Where? Will see a solution in monitors
later.
46Problems with Semaphores
- Incorrect use of semaphore operations
- signal (mutex) . wait (mutex)
- wait (mutex) wait (mutex)
- Omitting of wait (mutex) or signal (mutex) (or
both)
47Monitors
- An attempt to provide better tools for
synchronization problems - Specialized form of ADT
- Encapsulates implementation
- Public interface (types functions)
- Only one process can be executing in the ADT at a
time - other processes that try to use the monitor are
forced to wait
monitor anADT semaphore mutex 1 //
Implicit . . . public proc_i()
P(mutex) // Implicit ltprocessing for
proc_igt V(mutex) // Implicit . .
.
Conceptually how monitors are implemented. Actual
implementations vary
48Monitors
- Only one process may be active within the monitor
at a time - monitor monitor-name
-
- // shared variable declarations
- procedure P1 () .
-
- procedure Pn ()
- Initialization code ( .)
-
-
49Schematic view of a Monitor
50Example1 Shared Balance
programmer does not have to use semaphores only
one process can be in any function of the monitor
at a time. Implicit semaphores are added by the
compiler.
monitor sharedBalance double
balance public credit(double amount) balance
amount debit(double amount) balance -
amount . . . In process 1
code sharedBalance.credit(100) In process 2
code sharedBalance.debit(50)
51Example2 Readers Writers
Solution outline
monitor readerWriter_1 int numberOfReaders
0 int numberOfWriters 0 boolean busy
FALSE public startRead()
finishRead() startWrite()
finishWrite()
reader() while(TRUE) . . .
startRead() finishRead() . .
. fork(reader, 0) . . . fork(reader,
0) fork(writer, 0) . . . fork(writer, 0)
writer() while(TRUE) . . .
startWrite() finishWrite() . . .
52Example Readers Writers
monitor readerWriter_1 int numberOfReaders
0 int numberOfWriters 0 boolean busy
FALSE public startRead()
while(numberOfWriters ! 0)
numberOfReaders finishRead()
numberOfReaders--
startWrite() numberOfWriters
while( busy
(numberOfReaders gt 0) ) busy
TRUE finishWrite()
numberOfWriters-- busy FALSE
53Example Readers Writers
monitor readerWriter_1 int numberOfReaders
0 int numberOfWriters 0 boolean busy
FALSE public startRead()
while(numberOfWriters ! 0)
numberOfReaders finishRead()
numberOfReaders--
startWrite() numberOfWriters
while( busy
(numberOfReaders gt 0) ) busy
TRUE finishWrite()
numberOfWriters-- busy FALSE
54Sometimes Need to Suspend
- Process obtains monitor, but detects a condition
for which it needs to wait - Want special mechanism to suspend until condition
is met, then resume - Process that makes condition true must exit
monitor - Suspended process then resumes
- Condition Variable
55 Monitor with Condition Variables
56Condition Variables
- Essentially an event (as defined previously)
- Occurs only inside a monitor
- Operations to manipulate condition variable
- wait Suspend invoking process until another
executes a signal - signal Resume one process if any are suspended,
otherwise do nothing - queue Return TRUE if there is at least one
process suspended on the condition variable - Example
- condition x, y
- x.wait ()
- x.signal ()
57Condition Variables
- Two ways to implement condition variables
- Hoares semantics
- p1 does a wait
- p0 signals
- p0 is immediately suspended, p1 is immediately
resumed - when p1 finishes and leaves the monitor, p0
continues
58Condition Variables
- Two ways to implement condition variables
- Brinch Hansens semantics
- p1 does a wait
- p0 signals
- p0 finishes and leaves the monitor
- when p0 leaves the monitor, p1 checks to see if
its condition still holds. If it does, p1
continues in the monitor.
59Active vs Passive signal
- Hoare semantics same as active semaphore
- p0 executes signal while p1 is waiting ? p0
yields the monitor to p1 - The signal is only TRUE the instant it happens
- Brinch Hansen (Mesa) semantics same as passive
semaphore - p0 executes signal while p1 is waiting ? p0
continues to execute, then when p0 exits the
monitor p1 can receive the signal - Used in the Xerox Mesa implementation
60Hoare vs Mesa Semantics
condition variable
- Hoare semantics
- Use if to check if resource is available.
. . . if(resourceNotAvailable())
resourceCondition.wait() / now available
continue / . . .
- Mesa semantics
- Must loop because must check to see if resource
is still available when we finally get the signal
. . . while(resourceNotAvailable())
resourceCondition.wait() / now available
continue / . . .
612nd Try at Readers Writers
monitor readerWriter_2 int numberOfReaders
0 boolean busy FALSE condition okToRead,
okToWrite public startRead() if(busy
(okToWrite.queue()) okToRead.wait()
numberOfReaders okToRead.signal()
finishRead() numberOfReaders--
if(numberOfReaders 0) okToWrite.signal()
startWrite() if((numberOfReaders ! 0)
busy) okToWrite.wait() busy
TRUE finishWrite() busy FALSE
if(okToRead.queue()) okToRead.signal()
else okToWrite.signal()
62Example Synchronizing Traffic
- One-way tunnel
- Can only use tunnel if no oncoming traffic
- OK to use tunnel if traffic is already flowing
the right way - In class
- Problem 1 make the synchronization work (dont
worry about fairness) - Problem 2 add fairness
63Example Synchronizing Traffic
monitor tunnel int northbound 0, southbound
0 trafficSignal nbSignal RED, sbSignal
GREEN condition busy public nbArrival()
if(southbound gt 0) busy.wait()
northbound nbSignal GREEN sbSignal
RED sbArrival() if(northbound gt 0)
busy.wait() southbound nbSignal
RED sbSignal GREEN depart(Direction
exit) ( if(exit NORTH
northbound-- if(northbound 0)
while(busy.queue()) busy.signal() else
if(exit SOUTH) southbound--
if(southbound 0) while(busy.queue())
busy.signal()
64Dining Philosophers again ...
- Use an array state to indicate state of the
philosophers - statei thinking when philosopher i is
thinking - When philosopher i wishes to eat, calls the
monitor function pickUpForks(i) - only allowed to pick up a fork if both adjacent
forks are available - use the private function test( )
- philosopher moves to eating state only if two
neighbors are not in eating state. - otherwise waits for a signal
- both philosophers on either side have to call
putDownForks( ) before philosopher can move from
hungry to eating state.
65Dining Philosophers again ...
define N ___ enum status(EATING, HUNGRY,
THINKING monitor diningPhilosophers status
stateN condition selfN test(int i)
if((state(i-1) mod N ! EATING)
(statei HUNGRY) (state(i1) mod
N ! EATING)) statei EATING
selfi.signal() public
diningPhilosophers() // Initilization
for(int i 0 i lt N i) statei THINKING
66Dining Philosophers again ...
test(int i) if((state(i-1) mod N !
EATING) (statei HUNGRY)
(state(i1) mod N ! EATING)) statei
EATING selfi.signal()
public diningPhilosophers() ...
pickUpForks(int i) statei HUNGRY
test(i) if(statei ! EATING)
selfi.wait() putDownForks(int i)
statei THINKING test((i-1) mod N)
test((i1) mod N)
67Experience with Monitors
- Danger of deadlock with nested calls
- call another monitor from within a monitor
- example try to call a function in monitor B from
within a function in monitor A - but what if some other process is currently in
monitor A and wants into monitor B? - same problem as with nested semaphores
68Experience with Monitors
- Implementation of monitors
- not in UNIX (some versions support similar
mechanisms) - some languages support (Modula-3 and Java)
- Monitors were implemented in Xeroxs Mesa
programming language - Used Brinch Hansen semantics
- Nested monitor calls are, in fact, a problem
- which wait implementation is best?
- how is priority scheduling done?
- Needed timeouts, aborts, etc.
- See paper by Lampson Redell
69Atomic Transactions
- Sometimes would like to make sure that a critical
section forms a single logical unit of work - Either is performed in entirety or not at all
- Consistency of data is a concern associated with
database systems - Recent trend store all user information in OS
database - Makes retrieval and search fast and effective
- Many OS are trying to implement this OS X,
Vista, etc. - Thus it is useful to consider DB techniques used
to assure atomicity. - We wont go there. See your book, section 6.9
70Synchronization Examples
- Solaris
- Windows XP
- Linux
- Pthreads
71Solaris Synchronization
- Implements a variety of locks to support
multitasking, multithreading (including real-time
threads), and multiprocessing - Uses adaptive mutexes for efficiency when
protecting data from short code segments - Uses condition variables and readers-writers
locks when longer sections of code need access to
data - Uses turnstiles to order the list of threads
waiting to acquire either an adaptive mutex or
reader-writer lock
72Windows XP Synchronization
- Uses interrupt masks to protect access to global
resources on uniprocessor systems - Uses spinlocks on multiprocessor systems
- Also provides dispatcher objects which may act as
either mutexes and semaphores - Dispatcher objects may also provide events
- An event acts much like a condition variable
73Linux Synchronization
- Linux
- disables interrupts to implement short critical
sections - Linux provides
- semaphores
- spin locks
74Semaphores in Linux
- Interprocess communication facilities were
introduced in the ATT System V.2 release of
UNIX. - These facilities have a similar programming
interface and are thus referred to as the IPC
facilities or System V IPC. - There are now other ways of communicating between
processes (eg, pipes). - The three System V IPC mechanisms
- Semaphores
- Message Queues
- Shared Memory
- Well only talk about semaphores here.
75Semaphores in Linux
- The LINUX semaphore interface is elaborate.
- offers far more facilities than are generally
required - All UNIX semaphore functions operate on arrays of
general semaphores.
76Semaphores in Linux
- Semaphore functions
- include ltsys/sem.hgt
- int semctl(int sem_id, int sem_num, int command,
) - int semget(key_t key, int num_sems, int
sem_flags) - int semop(int sem_id, struct sembuf sem_ops,
size_t num_sem_ops)
77Semaphores in Linux
- useful include files
- include ltunistd.hgt
- include ltsys/types.hgt
- include ltsys/ipc.hgt
- include ltsys/sem.hgt
78Semaphores in Linux
- We do not need such powerful semaphores.
- Will use an abstraction that creates general
semaphores from the Linux semaphore facilities. - You can find details about the Linux semaphore
facilities in the Beginning Linux Programming
book.
79Semaphores in Linux
- The functions available in our abstraction
- static int start_semvalue(int sem_num)
- creates a semaphore and returns a semaphore ID.
Different processes can access the same semaphore
by using the same sem_num. - static int set_semvalue(int sem_id,int semVal)
- sets the value of semaphore sem_id to be semVal.
80Semaphores in Linux
- The functions available in our abstraction
- static void del_semvalue(int sem_id)
- removes a semaphore identifier. Does not delete
the semaphore other processes can still use it. - static int semaphore_p(int sem_id)
- does a P operation on sem_id.
- static int semaphore_v(int sem_id)
- does a V operation on sem_id.
81Semaphores in LinuxbinSem.h
- static int start_semvalue(int sem_num)
-
- return( semget((key_t)sem_num, 1, 0666
IPC_CREAT)) -
82Semaphores in LinuxbinSem.h
- static int set_semvalue(int sem_id,int semVal)
-
- union semun
- int val
- struct semid_ds buf
- ushort array
- sem_union
- sem_union.val semVal
- if (semctl(sem_id, 0, SETVAL,sem_union)
-1) return(0) - return(1)
-
83Semaphores in LinuxbinSem.h
- static void del_semvalue(int sem_id)
-
- union semun
- int val
- struct semid_ds buf
- ushort array
- sem_union
-
- if (semctl(sem_id, 0, IPC_RMID, sem_union)
-1) - fprintf(stderr, "Failed to delete semaphore
\n")
84Semaphores in LinuxbinSem.h
- static int semaphore_p(int sem_id)
-
- struct sembuf sem_b
-
- sem_b.sem_num 0
- sem_b.sem_op -1 / P() /
- sem_b.sem_flg SEM_UNDO
- if (semop(sem_id, sem_b, 1) -1)
- fprintf(stderr, "semaphore_p failed\n")
- return(0)
-
- return(1)
85Semaphores in LinuxbinSem.h
- static int semaphore_v(int sem_id)
-
- struct sembuf sem_b
-
- sem_b.sem_num 0
- sem_b.sem_op 1 / V() /
- sem_b.sem_flg SEM_UNDO
- if (semop(sem_id, sem_b, 1) -1)
- fprintf(stderr, "semaphore_v failed\n")
- return(0)
-
- return(1)
86Semaphores in Linuxexample
- include ltstdlib.hgt
- include ltstdio.hgt
- include ltsys/types.hgt
- include ltsys/ipc.hgt
- include ltsys/sem.hgt
- include "binSem.h"
- static int sem_id
87Semaphores in Linuxexample
- int main(int argc, char argv)
- int i
- int pause_time
- char op_char 'O'
- srand((unsigned int)getpid())
- sem_id start_semvalue(1234)
- if (argc gt 1)
- if (!set_semvalue(sem_id,1))
- fprintf(stderr, "Failed to initialize
semaphore\n") - exit(EXIT_FAILURE)
-
- op_char 'X'
- sleep(2)
-
To run this program, execute it once in
the background with a command line parameter,
then execute it again with no parameter. gtgcc
ex1.c gt a.out 1 1 1082 gt a.out 00XX00XX00XX000
0 1083 finished 1082 finished gt
output will be even numbers of Xs interspersed
with even numbers of 0s
88Semaphores in Linuxexample
- for(i 0 i lt 10 i)
- if (!semaphore_p(sem_id))
exit(EXIT_FAILURE) - / start of critical section /
- printf("c", op_char)fflush(stdout)
- pause_time rand() 3
- sleep(pause_time)
- printf("c", op_char)fflush(stdout)
- / end of critical section /
- if (!semaphore_v(sem_id))
exit(EXIT_FAILURE) -
- pause_time rand() 2
- sleep(pause_time)
-
-
89Semaphores in Linuxexample
- printf("\nd - finished\n", getpid())
- if (argc gt 1)
- sleep(10)
- del_semvalue(sem_id)
-
-
- exit(EXIT_SUCCESS)
-
-
90Semaphores in Linuxexample 2
- In this example, a child is forked off.
- The parent creates and initializes the semaphore
before creating the child. - The child and parent each have a CS.
- both open the same file in their CS.
- The parent writes two Ps into the file
- The child writes two Cs into the file
- Each CS is inside a loop. Both parent and child
loop 10 times.
91Semaphores in Linuxexample 2
- include "binSem.h"
- static int sem_id
- int main(int argc, char argv)
- int i
- int pause_time
- char op_char 'C'
- FILE fd
- char name"temp2.txt"
- srand((unsigned int)getpid())
- sem_id start_semvalue(1234)
- if (!set_semvalue(sem_id,1) )
- fprintf(stderr, "Failed to initialize
semaphore\n") - exit(EXIT_FAILURE)
-
-
92Semaphores in Linuxexample 2
The child code
- if (fork() 0)
- for(i 0 i lt 10 i)
- if (!semaphore_p(sem_id))
exit(EXIT_FAILURE) - / start of critical section /
- fd fopen(name, "a")
- fprintf(fd, "c", op_char)fflush(stdout
) - pause_time rand() 3
- sleep(pause_time)
- fprintf(fd, "c", op_char)fflush(stdout
) - fclose(fd)
- / end of critical section /
- if (!semaphore_v(sem_id))
exit(EXIT_FAILURE) - pause_time rand() 2
- sleep(pause_time)
-
- exit(0)
-
93Semaphores in Linuxexample 2
The parent code
- / we're in the parent's code /
- op_char 'P'
- for(i 0 i lt 10 i)
- if (!semaphore_p(sem_id))
exit(EXIT_FAILURE) - / start of critical section /
- fd fopen(name, "a")
- fprintf(fd, "c", op_char)fflush(stdout
) - pause_time rand() 3
- sleep(pause_time)
- fprintf(fd, "c", op_char)fflush(stdout
) - fclose(fd)
- / end of critical section /
- if (!semaphore_v(sem_id))
exit(EXIT_FAILURE) - pause_time rand() 2
- sleep(pause_time)
-
94Semaphores in Linuxexample 2
- wait(NULL)
- printf("\nd - finished\n", getpid())
- del_semvalue(sem_id)
- exit(EXIT_SUCCESS)
95Pthreads Synchronization
- Pthreads API is OS-independent
- It provides
- mutex locks
- condition variables
- Non-portable extensions include
- read-write locks
- spin locks
96Pthreads Synchronization
- Semaphores in pthreads
- Semaphore functions start with sem_
- Must include the library
- include ltsemaphore.hgt
- initializing (for binary semaphores)
- int sem_init(sem_t sem, int pshared, unsigned
int value) - sem is the semaphore
- pshared is the type of semaphore
- if 0, then semaphore is local to the current
process - O.W. the semaphore may be shared between processes
97Pthreads Synchronization
- initializing (for binary semaphores)
- int sem_wait(sem_t sem)
- int sem_post(sem_t sem)
- sem is thepointer to a semaphore object
98Pthreads Semaphores Example
- include ltstdio.hgt
- include ltunistd.hgt
- include ltstdlib.hgt
- include ltpthread.hgt
- include ltsemaphore.hgt
- void thread_function(void arg)
- sem_t bin_sem
- define WORK_SIZE 1024
- char work_areaWORK_SIZE
- int main()
- int res
- pthread_t a_thread
- void thread_result
-
99Pthreads Semaphores Example
- res sem_init(bin_sem, 0, 0)
- if (res ! 0)
- perror("Semaphore initialization
failed") - exit(EXIT_FAILURE)
-
- res pthread_create(a_thread, NULL,
thread_function, NULL) - if (res ! 0)
- perror("Thread creation failed")
- exit(EXIT_FAILURE)
-
- printf("Input some text. Enter 'end' to
finish\n") - while(strncmp("end", work_area, 3) ! 0)
- fgets(work_area, WORK_SIZE, stdin)
- sem_post(bin_sem)
-
- printf("\nWaiting for thread to
finish...\n") - res pthread_join(a_thread, thread_result)
- if (res ! 0)
- perror("Thread join failed")
100Pthreads Semaphores Example
- printf("Thread joined\n")
- sem_destroy(bin_sem)
- exit(EXIT_SUCCESS)
-
- void thread_function(void arg)
- sem_wait(bin_sem)
- while(strncmp("end", work_area, 3) ! 0)
- printf("You input d characters\n",
strlen(work_area) -1) - sem_wait(bin_sem)
-
- pthread_exit(NULL)
101Pthreads Mutex Example
- include ltstdio.hgt
- include ltunistd.hgt
- include ltstdlib.hgt
- include ltpthread.hgt
- include ltsemaphore.hgt
- void thread_function(void arg)
- pthread_mutex_t work_mutex / protects both
work_area and time_to_exit / - define WORK_SIZE 1024
- char work_areaWORK_SIZE
- int time_to_exit 0
102Pthreads Mutex Example
- int main()
- int res
- pthread_t a_thread
- void thread_result
- res pthread_mutex_init(work_mutex, NULL)
- if (res ! 0)
- perror("Mutex initialization failed")
- exit(EXIT_FAILURE)
-
- res pthread_create(a_thread, NULL,
thread_function, NULL) - if (res ! 0)
- perror("Thread creation failed")
- exit(EXIT_FAILURE)
-
- pthread_mutex_lock(work_mutex)
- printf("Input some text. Enter 'end' to
finish\n") -
103Pthreads Mutex Example
- while(!time_to_exit)
- fgets(work_area, WORK_SIZE, stdin)
- pthread_mutex_unlock(work_mutex)
- while(1)
- pthread_mutex_lock(work_mutex)
- if (work_area0 ! '\0')
- pthread_mutex_unlock(work_mutex)
- sleep(1)
-
- else
- break
-
-
-
- pthread_mutex_unlock(work_mutex)
- printf("\nWaiting for thread to
finish...\n") - res pthread_join(a_thread, thread_result)
- if (res ! 0)
- perror("Thread join failed")
104Pthreads Mutex Example
- void thread_function(void arg)
- sleep(1)
- pthread_mutex_lock(work_mutex)
- while(strncmp("end", work_area, 3) ! 0)
- printf("You input d characters\n",
strlen(work_area) -1) - work_area0 '\0'
- pthread_mutex_unlock(work_mutex)
- sleep(1)
- pthread_mutex_lock(work_mutex)
- while (work_area0 '\0' )
- pthread_mutex_unlock(work_mutex)
- sleep(1)
- pthread_mutex_lock(work_mutex)
-
-
- time_to_exit 1
- work_area0 '\0'
- pthread_mutex_unlock(work_mutex)
- pthread_exit(0)
105End of Chapter 6