6 Semaphores

14
1 ©2011 by Kevin Jeffay COMP 530 Introduction to Operating Systems Semaphores Kevin Jeffay Department of Computer Science University of North Carolina at Chapel Hill [email protected] September 21, 2011 http://www.cs.unc.edu/~jeffay/courses/comp530 2 ©2011 by Kevin Jeffay Lecture 6: Semaphores Outline and key concepts Semaphore abstract data type Mutual exclusion versus condition synchronization Binary semaphores General / counting semaphores Semaphore implementations with busy-waiting Semaphore implementations with an OS kernel Readings: » Chapter 6 (Process Synchronization)

Transcript of 6 Semaphores

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

» …