Semaphores Synchronization tool (provided by the OS) that does not require busy waiting. Logically, a semaphore S is an integer variable that, apart from initialization, can only be accessed through 2 atomic and mutually exclusive operations: wait(S) signal(S)
Critical Section of n Processes Shared data: semaphore mutex; //initially mutex = 1 Process Pi: do { wait(mutex); critical section signal(mutex); remainder section } while (1);
Semaphores Access is via two atomic operations: wait (S): while S 0 do no-op; S--; signal (S): S++; However, to avoid busy waiting: when a process has to wait, it will be put in a blocked queue of processes waiting for the same event.
Semaphores Hence, in fact, a semaphore is a record (structure): type semaphore = record count: integer; queue: list of process end; var S: semaphore; When a process must wait for a semaphore S, it is blocked and put on the semaphore’s queue. The signal operation removes (assume a fair policy like FIFO) one process from the queue and puts it in the list of ready processes.
Semaphore Implementation Define semaphore as a structure: typedef struct { int count; struct process *L; } semaphore; Assume two simple operations: block suspends the process that invokes it. wakeup(P) resumes the execution of a blocked process P.
Semaphore as a General Synchronization Tool Execute B in Pj only after A executed in Pi. Use semaphore flag initialized to 0. Code: Pi Pj A wait(flag) signal(flag) B
Deadlock and Starvation Deadlock – two or more processes are waiting indefinitely for an event that can be caused by only one of waiting processes. Let S and Q be two semaphores initialized to 1 P0 P1 wait(S); wait(Q); wait(Q); wait(S); signal(S); signal(Q); signal(Q) signal(S); Starvation – indefinite blocking. A process may never be removed from the semaphore queue in which it is suspended.
Two Types of Semaphores Binary semaphore – integer value can range only between 0 and 1; can be simpler to implement. Counting semaphore – integer value can range over an unrestricted domain. We can implement a counting semaphore S as a binary semaphore (protecting its counter).
Binary Semaphores We have also binary semaphores the semaphore can have only two values 0 or 1. counting semaphores can be implemented by binary semaphores. generally more difficult to use than counting semaphores (e.g., they cannot be initialized to an integer k > 1).
Binary semaphores waitB(S): if (S.value = 1) { S.value := 0; } else { block this process place this process in S.queue } signalB(S): if (S.queue is empty) { S.value := 1; } else { remove a process P from S.queue place this process P on ready list }
Counting Semaphore’s operations wait(S): S.count--; if (S.count<0) { block this process place this process in S.queue } signal(S): S.count++; if (S.count<=0) { remove a process P from S.queue place this process P on ready list }
Implementation Semaphore operations now defined as wait(S): S.value--; if (S.value < 0) { add this process to S.L; block; } signal(S): S.value++; if (S.value <= 0) { remove a process P from S.L; wakeup(P);
Implementing Counting Semaphore with Binary Semaphore Data structures: binary-semaphore S1, S2; int C; Initialization: S1 = 1 S2 = 0 C = initial value of semaphore S
Implementing Counting Semaphore with Binary Semaphore wait operation: waitb(S1); C--; if (C < 0) { signalb(S1); waitb(S2); } signal operation: C ++; if (C <= 0) signalb(S2); else signalb(S1);
Problems with Semaphores Semaphores provide a powerful tool for synchronization and enforcing mutual exclusion and coordinate processes. But wait(S) and signal(S) are scattered among several processes. Hence, difficult to understand their effects. Usage must be correct in all the processes. One bad (or malicious) process can fail the entire collection of processes.