Interprocess Communication in Operating Systems: Semaphore

Interprocess Communication in Operating Systems: Semaphore

Description:
A semaphore is an integer variable used for process/thread synchronization, controlling access to shared resources through two atomic operations (wait and signal). Proposed by Dutch computer scientist Dijkstra in 1965 to solve the critical section problem, it is one of the most fundamental synchronization primitives in operating systems.

Key Points:

  1. A semaphore is a non-negative integer representing the number of available resources.
  2. Supports two atomic operations: P operation (wait/proberen) and V operation (verhogen/signal).
  3. Divided into binary semaphores (value 0 or 1) and counting semaphores (value can be any non-negative integer).

Detailed Explanation:

1. Basic Concept of Semaphores
A semaphore is essentially a counter that records the current number of available resources of a certain type. When a process needs to access a shared resource, it first performs a P operation on the semaphore (request resource); after use, it performs a V operation (release resource).

2. Atomic Operations of Semaphores
The core of semaphores lies in the atomicity of their operations—meaning the operation cannot be interrupted during execution.

  • P operation (wait operation):
P(semaphore S):
    while S <= 0 do
        // Wait until S > 0
    S = S - 1
  • V operation (signal operation):
V(semaphore S):
    S = S + 1

3. Blocking Mechanism in Practical Implementation
The busy-waiting implementation above is inefficient; actual operating systems use blocking queues:

// Actual semaphore data structure
struct semaphore {
    int value;              // Current value of the semaphore
    ProcessQueue queue;     // Queue of processes waiting for this semaphore
}

P(semaphore S):
    S.value = S.value - 1   // Decrement first
    if S.value < 0:
        // Insufficient resources, add current process to wait queue and block
        add current_process to S.queue
        block(current_process)  // Process enters blocked state

V(semaphore S):
    S.value = S.value + 1   // Increment first
    if S.value <= 0:
        // There are processes waiting, wake one up
        remove a process P from S.queue
        wakeup(P)           // Wake up the blocked process

4. Types and Applications of Semaphores

Binary Semaphore (Mutex Lock):

  • Value can only be 0 or 1, used for mutual exclusion access.
  • Initial value is typically set to 1 (indicating resource is available).
  • Example: Protecting a critical section.
semaphore mutex = 1;  // Initialization

// Process A
P(mutex);            // Enter critical section
// Access shared resource
V(mutex);            // Exit critical section

// Process B  
P(mutex);
// Access shared resource
V(mutex);

Counting Semaphore:

  • Value can be any non-negative integer, used for resource pool management.
  • Initial value is set to the total number of available resources.
  • Example: Controlling a buffer pool with 5 buffers.
semaphore empty = 5;  // Initial number of empty buffers
semaphore full = 0;    // Initial number of full buffers

// Producer process
while(true) {
    P(empty);          // Request an empty buffer
    // Produce data and place into buffer
    V(full);           // Increment full buffer count
}

// Consumer process
while(true) {
    P(full);           // Request a full buffer  
    // Take data from buffer and consume
    V(empty);          // Increment empty buffer count
}

5. Key Points in Semaphore Implementation

  • Atomicity Guarantee: P and V operations must be executed within a critical section, typically achieved by disabling interrupts or using hardware atomic instructions.
  • Avoid Busy Waiting: When resources are unavailable, processes should be blocked rather than continuously checking.
  • Wake-up Strategy: Usually, a FIFO strategy is adopted to avoid starvation.

6. Advantages and Disadvantages of Semaphores

Advantages:

  • Powerful functionality, capable of solving various synchronization problems.
  • Clear concept, easy to understand and use.
  • Supports complex synchronization patterns.

Disadvantages:

  • Improper use can easily lead to deadlock.
  • Higher programming complexity, prone to errors.
  • Requires programmers to manually manage semaphore operations.

Semaphores provide a powerful process synchronization mechanism for operating systems and are an important foundation for implementing concurrency control in modern multitasking operating systems.