Semaphores are a synchronisation tool, proposed by E.W. Dijkstra that :
- simplifies synchronisation for the programmer
- does not require (much) busy waiting, and
- can guarantee bounded waiting time and progress
Consists of :
- A semaphore type
S, that records a list of waiting processes and an integer - Two standard atomic ( very important) operations by which to modify
S:wait()andsignal()
Works like :
- The semaphore is initialised with a count value of the maximum number of processes allowed in the critical section at the same time.
- When a process calls
wait(), if count is zero, it adds itself to the list of sleepers and blocks, else it decrements count and enters the critical section - When a process exits the critical section it calls
signal(), which increments count and issues a wakeup call to the process at the head of the list, if there is such a process.- It is the use of ordered wake-ups (e.g. FIFO) that makes semaphores support bounded (i.e. fair) waiting.
Semaphore as General Synchronisation Tool
- We can describe a particular semaphore as :
- A Counting semaphore - integer value can range over an unrestricted domain (e.g. allow at most N threads to read a database, etc.)
- A Binary semaphore - integer value can range only between 0 and 1
- Also known as mutex locks, since ensure mutual exclusion.
- Basically, it is a counting semaphore initialised to 1.
Critical Section Solution with Semaphore
Semaphore mutex; // Initialized to 1
do {
wait(mutex); // Unlike the pure spin-lock, we are blocking here.
[critical section]
signal(mutex);
[remainder section]
} while (TRUE);Semaphore Implementation : State and Wait
We can implement a semaphore within the kernel as follows (note that the functions must be atomic, which our kernel must ensure):
typedef struct {
int count;
process_list; // Hold a list of waiting processes/threads
} Semaphore;
void wait(Semaphore *S) {
S->count--;
if (S->count < 0) {
add process to S->process_list;
sleep();
}
}- Note that, here, we decrement the wait counter before blocking (unlike the previous description).
- This does not alter functionality but has the useful side-effect that the negative count value reveals how many processes are currently blocked on the semaphore.
Semaphore Implementation : Signal
void signal(Semaphore *S) {
S->count++;
if (S->count <= 0) { // If at least one waiting process, let him in.
remove next process, P, from S->process_list
wakeup(P);
}
}