Semaphores can only be accessed by two operations (they will be system calls)
P(S): trap to the kernel
disable interrupts (so semaphore access is atomic)
if S > 0 then S := S - 1
else { queue the process on S, change its state to blocked,
schedule another process }
enable interrupts
return
V(S): trap to the kernel
disable interrupts
if S = 0 and queue on S is not empty then
{ pick a process from the queue on S,
change its state from blocked to ready }
else S := S + 1
enable interrupts
return
Now we can implement
Semaphores can be used in two ways
P(S) count++ V(S) /* S only takes on values 0, 1 */
S is called a binary semaphore
P(empty) ... V(full)
P(full) ... V(empty)
the semaphore represents the number of resources available and is
called a counting semaphore
fn.
Why is the semaphore mutex also used in the auditor process?
Isn't is enough just to make the moving of money from one account to
another in the ATM process into a single atomic action?
SR Program: A Semaphore Prevents Another Race Condition.
Semaphores can be used for process synchronization that is not for mutual exclusion. For example, this program starts three processes: Pa, Pb, and Pc. Only Pa can output the letter A, Pb B, and Pc C. Utilizing semaphores (and no other variables) the processes are synchronized so that the output satisfies the following conditions:
This is an SR program simulating the bounded buffer producer and consumer
classical OS problem.
There is one producer and one consumer process that share a buffer of some
finite number of slots.
Three semaphores are used:
one for mutual exclusion
on updating the value of the variable count
and two for condition synchronization (to keep a producer from putting
something in a full buffer and to keep a consumer from taking something
out of an empty buffer).
SR Program: Bounded Buffer Producer and Consumer.
Bounded buffers are useful for many things. For example, if you have a bunch of computations that can each be broken into several parts, each part taking about the same amount of time, then the series of computations can be performed in an assembly line, conveyor belt, or pipeline fashion. Each part will be handled by its own process. If a CPU can be dedicated to each process, then the series of computations can be performed faster.
The same basic idea is used in the vector or pipeline units in the Cray architecture. Adding two floating point vectors is a series of computations: C[i] = A[i] + B[i] for i = 1, 2, ..., N. Each addition operation can be broken into several parts or suboperations: compare the exponents, align the mantissas, etc. Each part can be done to the pairs of vector components in an assembly line fashion as the pairs stream into the vector unit.
This SR example program starts three processes.
The first creates a random real number to which three operations or steps
need to be performed: take the square root, then the sin, then square the
number.
Each of the three steps is performed in a process.
Random SR naps are done to make each step take some real time.
The first two processes use two bounded buffers to communicate their results
to the next process.
The bounded buffer can hold several intermediate results if the depositing
process gets ahead of the fetching process.
SR Program: Bounded Buffer Pipeline.
For those interested in Java, here is the same
bounded buffer producer and consumer program rewritten in Java.
Included is an example compile and run.
Note that since Java does not provide semaphores,
we have to write our own binary and counting semaphore classes.
Java Program: Bounded Buffer Producer and Consumer.
Goals of a solution to the dining philosophers problem
Bad solution
philosopher(i):
do true ->
think()
pick_up_left_fork()
pick_up_right_fork()
eat()
put_down_left_fork()
put_down_right_fork()
od
This can deadlock if all philosophers execute
pick_up_right_fork() at the same time.
Also suffers from starvation in the absence of contention if the philosophers get hungry in a ``bad'' order.
Even if you permit at most 4 of the 5 philosophers into the room to eat at any one time, this solution will still suffer from starvation in the absence of contention if they enter the room in a ``bad'' order.
Another bad solution
philosopher(i):
do true ->
think()
do not have_both_forks ->
pick_up_left_fork()
if right_fork_free ->
pick_up_right_fork()
[] put_down_left_fork()
fi
od
eat()
od
No more deadlock but now suffers from livelock.
Here is a solution using semaphores that
This semaphore solution to the readers-writers problem can let writers starve because
Sleeping barber
customers, barber, cutting
all initially zero waiting initially zero
barber: customers:
do true -> do true ->
P(customers) wander and grow hair for a while
waiting-- if waiting < number_chairs ->
V(barber) waiting++
P(cutting) V(customers)
od P(barber)
get hair cut for some time
V(cutting)
fi
od
mutex
to protect checking the value of waiting
to see if there is room in the waiting room and to protect
the ++ and -- of waiting
problem in this solution if multiple barbers:
one barber could race ahead of another after doing V(barber)
and before P(cutting) so a barber could
release one customer with V(barber)
but end up cutting another customer's hair with P(cutting)
Snapshot of the animation.
Have a look at the actual animation.
XTANGO Animation of Dining Philosophers.
If two philosophers collaborate, they can ``starve'' the one between them.
File Containing Animation Commands.
Snapshot of the animation.

Complete animation.
A Starving Philosopher.
SJH shartley@mcs.drexel.edu