Provides more sophisticated ways for processes synchronization.
Protect a critical section by semaphore S (count) – an integer variable
Accessed via two indivisible operations wait() & signal()
S: the count variable signifies how many available spots available for critical sections
S can be higher than 1 for some operations that allow multiple processes such as I/O read.
Types of Semaphore:
Counting semaphore: integer value can range over an unrestricted domain
Binary semaphore: integer value can range only between 0 and 1, which is Mutex Lock
Implementation with Busy wait
wait(): wait while count ⇐0, then when S— when executing in CS
wait(S) { while (S <= 0) ; // busywait S--; }
signal(): S++ means it is done running, so 1 more spot is available
signal(S) { S++;}
Requirement: No two processes can execute wait() & signal() on the same semaphore at the same time
However, the busy wait is very computationally expensive
Implementation without Busy wait:
Each Semaphore has an associated waiting queue
block(): place the process invoking the operation on the waiting queue
wakeup(): remove one of processes in the waiting queue, place it in the ready queue
wait(*S):
wait(Semaphore *S) { S->value--; if (S->value <0) { add this process to S->list; block(); }}
signal(*S):
signal (semaphore *S) ( S->value++; if (S->value <= 0) ( remove a process P from S->list; wakeup (P) ; }}
Problems with semaphore
signal(mutex) ... wait(mutex): where a process signals a mutex before waiting on it. This can lead to race conditions and incorrect behavior.
wait(mutex) ... wait(mutex): Multiple processes waiting on the same mutex without releasing it can cause a deadlock.
Difficult to detect
Omitting of wait(mutex) and/or signal(mutex): If a process forgets to wait on a mutex before accessing a shared resource, it can violate mutual exclusion and lead to inconsistencies.