1©2011 by Kevin Jeffay
COMP 530Introduction to Operating Systems
Semaphores
Kevin JeffayDepartment of Computer Science
University of North Carolina at Chapel [email protected]
September 21, 2011
http://www.cs.unc.edu/~jeffay/courses/comp530
2©2011 by Kevin Jeffay
Lecture 6: SemaphoresOutline and key concepts
Semaphore abstract data typeMutual exclusion versus condition synchronizationBinary semaphoresGeneral / counting semaphoresSemaphore implementations with busy-waitingSemaphore implementations with an OS kernel
Readings:» Chapter 6 (Process Synchronization)
3©2011 by Kevin Jeffay
Process CoordinationProblems with mutual exclusion algorithms
Mutual exclusion algorithms are complex andbrittle
All the algorithms we’ve seen so far employbusy waiting
process Pibegin loop inCS[i] := TRUE turn := i while turn = i AND inCS[j] do NOOP end while <critical section> inCS[i] := FALSE end loopend Pi
4©2011 by Kevin Jeffay
procedure down(sem)begin if (sem = 0) then <wait until sem > 0 > endif sem -= 1end down
SemaphoresA higher-level synchronization primitive
An abstract data type
» down()If sem > 0, then decrement sem by 1Otherwise “wait” until sem > 0 and then decrement
object semaphore exports down,up private sem : int procedure down() procedure up()end semaphore
Both operations are assumed tobe atomic
A non-negative integer variablewith two operations:
» up()Increment sem by 1
procedure up()begin sem += 1end up
procedure down()begin if (sem = 0) then <wait until sem > 0 > endif sem -= 1end down
(Also often called “P()”, “wait()”, ...)
(Also often called “V()”, “signal()”, ...)
5©2011 by Kevin Jeffay
Using SemaphoresSolving the critical section problem
Use a binary semaphore for mutual exclusion» The semaphore’s value is only ever 0 or 1
process P1begin mutex.down() <critical section> mutex.up()end P1
var mutex : binary_semaphore := 1
process P2begin mutex.down() <critical section> mutex.up()end P2
private sem : int
procedure down()begin if (sem = 0) then <wait until sem > 0 > endif sem := 0end down
procedure up()begin sem := 1end up
6©2011 by Kevin Jeffay
Using Semaphores For Mutual ExclusionProducer/Consumer synchronization
process Producerbegin loop <produce a character “c”> while count = n do NOOP end while buf[nextIn] := c nextIn := nextIn+1 mod n
count := count + 1
end loopend Producer
process Consumerbegin loop while count = 0 do NOOP end while data := buf[nextOut] nextOut := nextOut+1 mod n
count := count - 1
<consume “data”> end loopend Consumer
globalsbuf : array [0..n-1] of charnextIn,nextOut : 0..n-1 := 0
count : 0..n := 0
mutex.down()
mutex.down()
mutex.up()
mutex.up()
mutex : binary_semaphore := 1
7©2011 by Kevin Jeffay
Shared Memory Process CoordinationCondition Synchronization
process Producerbegin loop <produce a character “c”> while count = n do NOOP end while buf[nextIn] := input nextIn := nextIn+1 mod n
mutex.down() count := count + 1 mutex.up() end loopend Producer
process Consumerbegin loop while count = 0 do NOOP end while data := buf[nextOut] nextOut := nextOut+1 mod n
mutex.down() count := count - 1 mutex.up()
<consume “data”> end loopend Consumer
Two broad classes of synchronization problems:» Mutual exclusion» Condition synchronization — Awaiting the development of
a specific state within the computation
8©2011 by Kevin Jeffay
process Producerbegin loop <produce a character “c”>
buf[nextIn] := c nextIn := nextIn+1 mod n
mutex.down() count := count + 1 mutex.up() end loopend Producer
process Consumerbegin loop
data := buf[nextOut] nextOut := nextOut+1 mod n
mutex.down() count := count - 1 mutex.up()p()
<consume “data”> end loopend Consumer
<wait until a buffer is full>
<wait until a buffer is empty>
globals fullBuffers : semaphore := 0 emptyBuffers : semaphore := n
mutex : binary_semaphore := 1buf : array [0..n-1] of char nextIn,nextOut : 0..n-1 := 0count : 0..n := 0
Condition SynchronizationA producer/consumer system with counting semaphores
Counting semaphore: A semaphore whose value canbe greater than one (a.k.a. a general semaphore)
9©2011 by Kevin Jeffay
Condition SynchronizationA producer/consumer system with counting semaphores
process Producerbegin loop <produce a character “c”>
emptyBuffers.down() buf[nextIn] := c nextIn := nextIn+1 mod n fullBuffers.up() end loopend Producer
process Consumerbegin loop fullBuffers.down() data := buf[nextOut] nextOut := nextOut+1 mod n emptyBuffers.up()
<consume “data”> end loopend Consumer
globalsfullBuffers : semaphore := 0emptyBuffers : semaphore := n
buf : array [0..n-1] of char nextIn,nextOut : 0..n-1 := 0
nextOut nextInemptyBuffers fullBuffers
n-1
n-2
0 1 2 ...
10©2011 by Kevin Jeffay
Implementing SemaphoresHow to implement the “wait loop”?
private sem : int
procedure down()begin if (sem = 0) then <wait until sem > 0 > endif sem := sem – 1end down
procedure up()begin sem += 1end up
To implement semaphores we mustimplement the “wait” functionTwo approaches:» Use busy waiting (yuk!)» Use an operating systems kernel
11©2011 by Kevin Jeffay
Implementing SemaphoresBusy-waiting
To implement semaphores we mustimplement the “wait” function
procedure down()begin loop exit when(sem > 0) end loop
sem := sem - 1end down
private sem : int
procedure down()begin if (sem = 0) then <wait until sem > 0 > endif sem := sem – 1end down
procedure up()begin sem := sem + 1end up
First attempt: Use simple busy waiting
12©2011 by Kevin Jeffay
Implementing SemaphoresHardware-based solutions (I)
Disable interrupts to ensure the critical section isnonpreemptible» Nonpreemptible Indivisible Atomic
procedure down()begin loop DISABLE_INTS exit when(sem > 0) ENABLE_INTS end loop <interrupts are disabled>
sem := sem - 1 ENABLE_INTSend down
procedure up()begin DISABLE_INTS sem := sem + 1 ENABLE_INTSend up
13©2011 by Kevin Jeffay
Implementing SemaphoresHardware-based solutions (II)
Processors provide special instructions for mutualexclusion such as the test-and-set instruction» Performs a LOAD, COMPARE, and STORE in one indivisible
operation
function TST(value, var count : integer) : integerbegin TST := count if (count == value) then count := 0 endifend TST
Generaltest-and-set:
function TST(var flag : boolean) : booleanbegin TST := flag flag := FALSEend TST
Booleantest-and-set:
14©2011 by Kevin Jeffay
Implementing SemaphoresUsing test-and-set
Implementing a binary semaphore:» (Assume TRUE = 1, FALSE = 0)
private sem : boolean
procedure downb()begin while (NOT TST(sem)) do NOOP end whileend downb
procedure upb()begin sem := TRUEend upb
15©2011 by Kevin Jeffay
Using test-and-setImplementing general semaphores with binary semaphores
procedure up()begin
sem := sem + 1
end up
private sem : integer
procedure down()begin
if (sem = 0) then
end if sem := sem - 1
end down
Use two binary semaphores» (Assume binary semaphores are implemented with test-and-set)
“wait”
16©2011 by Kevin Jeffay
procedure up()begin
sem := sem + 1
end up
procedure down()begin
if (sem = 0) then
end if sem := sem - 1
end down
Implementing general sems with binary semsImplementation principles
General semaphore implementations are themselves acritical sectionThey must be protected with a binary semaphore» (Is there a critical section in the implementation of the binary
semaphore?)
mutex.downb()
mutex.upb()
mutex.downb()
“wait”
mutex.upb()
17©2011 by Kevin Jeffay
Implementing general sems with binary semsImplementation principles II
Condition synchronization is implemented with abinary semaphore called delay» (What should the initial value of delay be?)
procedure up()begin
sem := sem + 1
end up
procedure down()begin
if (sem = 0) then
end if sem := sem - 1
end down
mutex.downb()
mutex.upb()
“wait”
mutex.downb()
delay.downb()if (num_waiting delay.upb()end if
mutex.upb()
18©2011 by Kevin Jeffay
Implementing general sems with binary semsImplementation principles II
In down, you must release the mutual exclusion “lock”prior to waiting on the binary semaphore delay
procedure up()begin
sem := sem + 1
end up
procedure down()begin
if (sem = 0) then
end if sem := sem - 1
end down
mutex.downb()
mutex.upb()
mutex.upb()
mutex.downb()
delay.downb()mutex.downb()
delay.upb()
mutex.upb()
You must reacquire mutual exclusion upon returningfrom delay.downb
19©2011 by Kevin Jeffay
procedure down()begin
if (sem = 0) then
end if sem := sem - 1
end down
Implementing general sems with binary semsImplementation principles III
In up, one cannot unconditionally release a processwaiting on the delay binary semaphore» (Why not?)
procedure up()begin
sem := sem + 1
end up
num_waiting += 1
num_waiting -= 1
mutex.downb()
mutex.upb()
mutex.upb()
mutex.downb()
delay.downb()mutex.downb()
if (num_waiting delay.upb()end if
if (num_waiting > 0) then
end if
mutex.upb()
You must test that there exists a waiting process
20©2011 by Kevin Jeffay
Implementing general sems with binary semsImplementation principles IV
In down, to ensure the awaited condition is true uponreturn from down.delayb, the process signaling thecondition in up does not release mutual exclusion
procedure up()begin
sem := sem + 1
end up
procedure down()begin
if (sem = 0) then
end if sem := sem - 1
end down
num_waiting += 1
num_waiting -= 1
mutex.downb()
mutex.upb()
mutex.upb()
mutex.downb()
delay.downb()mutex.downb()
if (num_waiting delay.upb()end if
if (num_waiting > 0) then
else mutex.upb()end if
This means mutual exclusion does not need to be (andcannot be!) acquired after down.delayb
21©2011 by Kevin Jeffay
procedure down()begin mutex.downb() if (sem = 0) then num_waiting += 1 mutex.upb() delay.downb() mutex.downb() end if sem := sem - 1 mutex.upb()end down
Implementing gen. semaphores w/ bin. semsA non-“baton-passing” solution
Does this solution work?
procedure up()begin mutex.downb() sem := sem + 1 if (num_waiting > 0) then num_waiting -= 1 delay.upb() end if mutex.upb()end up
private sem : integer
delay : binary_semaphore := 0num_waiting : integer := 0
mutex : binary_semaphore := 1
22©2011 by Kevin Jeffay
Implementing SemaphoresUsing an operating system kernel
Eliminate busy-waiting through the use of multi-programming» When a process needs to block inside a down:
Suspend the currently executing processResume a ready process
RunningReady
Waiting
Head
Tail
system ready/runqueue
Head
Tail
semaphore queues(1 per semaphore)
running
23©2011 by Kevin Jeffay
Implementing Semaphores Using an OS KernelOS functions and structures
RunningReady
Waiting
Head
Tail
system ready/runqueue
Head
Tail
semaphore queues(1 per semaphore)
globals readyQueue : system_queue running : process_id
private mutex : binary_sem numWaiting : integer semQueue : system_queue sem : integer
down()
up() down() running
up()/down()
24©2011 by Kevin Jeffay
Implementing Semaphores Using an OS KernelUsing an operating system kernel
procedure down()begin mutex.downb()
if (sem = 0) then numWaiting += 1
DISABLE_INTS insert_queue(semQueue, running) next := remove_queue(readyQueue) mutex.upb() dispatch(next) ENABLE_INTS
numWaiting -= 1 end if
sem := sem - 1 mutex.upb()end down
procedure up()begin mutex.downb() sem := sem + 1
if (numWaiting > 0) then next := remove_queue(semQueue) DISABLE_INTS insert_queue(readyQueue, next) ENABLE_INTS else mutex.upb() end ifend up
Waiting
Head
Tail
systemreadyqueue
semaphore queues
RunningReady
HeadTail
running
25©2011 by Kevin Jeffay
Using Interrupts as a Binary SemaphoreExample: Implementing a context switch
context_switch(queue : system_queue)var next : process_idbegin DISABLE_INTS insert_queue(queue,runningProcess) next := remove_queue(readyQueue) dispatch(next) ENABLE_INTSend context_switch
dispatch(next : process_id)begin <save memory image of running > <save processor state of running > <load memory image of next> <load processor state of next> runningProcess := nextend dispatch
RunningReady
Waiting
Head
Tail
ready queue
Head
Tailsynchronizationqueues
running
26©2011 by Kevin Jeffay
Putting it all TogetherAn implementation of read() based on semaphores
An example ofinter-processcommunication andsynchronizationusing semaphores
Program1
Program2OS I/O
Device
read()
startIO()
interrupt
main{
read{
endio{
}schedule()
main{
}
}schedule()
27©2011 by Kevin Jeffay
Putting it all TogetherAn implementation of read() based on semaphores
process userProcessbegin : Read(string) :end userProcess
User Process
Read(var data : array of char)begin StartIO fullBuffers.down() data := OS_buffer[nextOut] nextOut += 1 mod nend Read
System Call
ttyIntHandlerbegin charBuffer[next] := device register next += 1 mod m intPending.upb()end ttyIntHandler
Interrupt Handler
process deviceDriverbegin loop intPending.downb() strcopy(OS_buffer[nextIn], charBuffer) nextIn += 1 mod n fullBuffers.up() end loopend deviceDriver
Kernel Process
28©2011 by Kevin Jeffay
Course Content Calibration TimeAre semaphores way cool or what?!
Semaphores the most complex thingwe’ll study in this course» So congrats on making it through the material!
If it soaked in then you should be able to saysomething intelligent about…» Atomic versus non-preemptible
» Binary versus counting semaphores
» Condition synchronization versus mutualexclusion
» …
Top Related