34
Semaphores Operating System support for concurrency

Semaphores Operating System support for concurrency

  • View
    234

  • Download
    0

Embed Size (px)

Citation preview

Semaphores

Operating System support for concurrency

Motivation

• Goal is to eliminate busy waiting– See Sec. 2.3.4, p. 108

• Consider a simplified version of the semaphore first:– Sleep – block if waiting to get into critical section

– Wake-up – unblock, or move from blocked to ready

• A little too simple– Vulnerable to race conditions…

Sleep and Wakeup

Producer-consumer problem with fatal race condition

Semaphores• A semaphore S is an integer variable that,

apart from initialization, is accessed only through two standard atomic operations wait (down) and signal (up) .– Atomic– S may be

• binary – used to enforce mutual exclusion (mutex) or synchronize processes;

• counting – used to synchronize processes

About semaphores...

• Invented by Edsgar Dijkstra in 1965– He was a famous Dutch computer scientist who

passed away in 2004…

• Generalized locking mechanisms– They count the number of saved wake-ups– May be implemented in hardware or software

• Got milk? example :wait(S) [or down(S)]if no milk then buy milksignal(S) [or up(S)]

wait & signal operations

wait (S): while S <= 0 do no-op; S = S - 1; as soon as S > 0

signal (S): S = S + 1;

• S is initialized to a value >= 1 why not 0? when >0?

• wait & signal are executed atomically – if one process is modifying the semaphore, no other

process can do so.

– in the wait, the testing of the semaphore and the modification is un-interruptible.

Semaphores are data structures

• They have the following basic operations associated with them:

– initialize

– wait

– signal

Recall the n-process Critical Section problem

• We can use a semaphore to guarantee simple mutual exclusion. S is initialized to 1, and the code for each process is as follows:

repeatwait (S)critical sectionsignal (S)remainder section

until false;

Easier than the bakery algorithm!

BACI implementation of semaphores

• semaphore S; declares S to be type semaphore

• initialsem(S, value) initializes semaphore S to a given value

• signal(S)

• wait(S)

Synchronizing processes

• Suppose p1 contains statement s1 and p2 contains statement s2

– s2 can execute only after s1 has completed

– it’s a synchronization issue

• Use semaphore sync, initialized to 0:

in p1: s1 in p2: wait (sync)

signal (sync) s2

Aren’t we still busy waiting?

• All solutions we’ve seen so far spend a lot of time doing nothing: while ..... do no-op;– This wastes CPU cycles that would otherwise

be available for other processes

• We address the problem of busy waiting by modifying the semaphore so that wait & signal enable a process to block itself rather than spin its wheels.

Recall process states

We will complicate this model slightly

READY RUNNING

BLOCKED

The block operation

• Puts the process (thread) in a semaphore’s queue. • Process state changes from running to blocked.

– It’s blocked waiting on the semaphore• Now another process or thread can execute.• The blocked process will become ready when

some other process executes a signal operation on the same semaphore. – Then, the process is placed in the ready queue.

How this is implemented• Define semaphore as a class object or struct:

• When a process must wait on a semaphore, it is added to its list of blocked processes. A signal operation removes one process from the list and wakes it up.

value List of waiting processes

Operations wait & signal are now more complexwait(S)

S.value := S.value - 1; if S.value < 0 then { add this process to S.list; block; }

signal(S)

S.value := S.value + 1; if S.value <= 0 then

{ remove a process

from S.list; wakeup(process);}

block & wakeup are provided by the OS (system calls)

Some observations• The order in which processes are awakened depends

on the CPU scheduling algorithm (not always FCFS)• Always remember that wait & signal are executed

atomically.– So, no two processes can execute wait or signal operations

on the same semaphore at the same time.

• If we have only one processor, we can inhibit interrupts during the time the wait and signal operations are executing. This will not work in a multiprocessing environment.

Some more observations

• Semaphore values in this later definition can actually become negative.– What would a negative value signify?

• Busy waiting has not actually been eliminated, just mitigated– it’s been moved to the wait & signal operations – why is this preferable?

Deadlock

• When we use a semaphore with a waiting queue, we may create a situation in which two or more processes are waiting for an event (a signal operation) that can be caused only by one of the waiting processes. When such a state is reached, these processes are said to be deadlocked.

• Formal def of deadlock in Chapter 3

Starvation

• Another related problem is indefinite postponement or starvation. This happens when processes must wait indefinitely and may occur if we add processes to (and remove them from) the semaphore list in LIFO order.

Some Classical Synchronization Problems (Sec. 2.4)

• These scenarios are used to test synchronization schemes:– Bounded Buffer– Readers Writers– Dining Philosophers– Sleeping Barber

• Solutions here rely on semaphores, but next time we’ll see another approach.

Examples of producer-consumer processes

Producer• Output process stores

characters in a buffer• Assembler process

produces object code

Consumer• Printer driver reads

characters from buffer• Linker/loader

process forms object code into a run unit

Bounded Buffer (Producer-Consumer) Problem

• Assume a buffer of size N.

• mutex (initialized to 1) – Provides mutual exclusion.

• empty and full (both semaphores) count the number of buffers that are empty and full resp. Empty initialized to N and full to 0– Used for synchronizing the processes

Producer Consumer problem

Readers and Writers

• If two (or more) readers access the shared file at the same time, there is no problem.

• However, we cannot allow a writer to access the shared file at the same time as another process (either a reader or writer). Why?

• So we give the writers exclusive access to the file.• BUT, no reader will be kept waiting unless a writer

has already gained access to the file• So, readers have a certain degree of priority over

writers...

The solution

• Initialize an integer variable rc to 0 – rc is used to count the number of readers– Mutual exclusion is required for any reader

process to update rc• Initialize two semaphores, mutex (mutual

exclusion while updating rc) and db (mutual exclusion for the writers), both to 1.

The Readers and Writers Problem

The Dining Philosophers

Models allocating several resources among several processes.

Correct solutions are free of both deadlock and starvation.

The problem

• We have 5 (or more) philosophers who eat & think all day. They all sit around a circular table that contains 5 plates and 5 single chopsticks, placed one between each plate.

• In order to eat, a philosopher needs 2 chopsticks, the one to the left of her plate and the one to the right.

• When she finishes eating she puts down both chopsticks.

A simple but flawed solution• Initialize all elements of semaphore array

chopstick to 1. (chopstick[0]...chopstick[4])repeat wait (chopstick[i]); wait (chopstick[(i+1) % 5]); eat signal (chopstick[i]); signal (chopstick[(i+1) % 5]); think;until false;

What can we do to fix this?

• Allow at most 4 philosophers to sit at the table set for five the same time

• Allow a philosopher to pick up her chopsticks only if both are available.

• Use an asymmetric solution i.e., an odd philosopher picks up the left and then the right, an even philosopher picks up the right and then the left.

Dining Philosophers solution

Part 1

Dining Philosophers solution

Part 2

The Sleeping Barber Problem

The Sleeping Barber solution