Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 7

Semaphores for Process Synchronization

Recommended resources

Hello! Let's dive into our next topic.

In our last lesson, we compared mutex locks and spinlocks, focusing on the trade-off between blocking a process (mutex) and making it busy-wait (spinlock). We concluded that while these are effective for simple mutual exclusion, they aren't expressive enough for more complex synchronization scenarios. For instance, how do we make a process wait until a buffer is no longer empty?

This lesson introduces semaphores, a more powerful and versatile synchronization tool developed by Edsger Dijkstra. They are a cornerstone of concurrent programming and a very frequent topic in the GATE exam. Our goal is to understand the two main types—binary and counting semaphores—and learn how to apply them to solve synchronization problems.

1. What is a Semaphore?

At its core, a semaphore is simply an integer variable that is shared among threads. However, it's a special variable because it can only be accessed via two atomic operations:

  1. wait(S) (also known as P(S) or down(S))
  2. signal(S) (also known as V(S) or up(S))

The letters P and V come from the Dutch words Proberen (to test) and Verhogen (to increment). The atomicity of these operations is critical—it ensures that while one process is modifying the semaphore value, no other process can interfere.

There are two main types of semaphores, which we will explore in detail:

  • Counting Semaphore: The integer value can range over an unrestricted domain.
  • Binary Semaphore: The integer value can range only between 0 and 1.

Let's begin with a short video that introduces semaphores and these fundamental operations.

GATE 2023 Exam | CSE | Operating Systems | Semaphores (Binary & Counting) | Free Online Course

The video 'GATE 2023 Exam | CSE | Operating Systems | Semaphores (Binary & Counting)' by BYJU'S Exam Prep provides a concise introduction tailored for GATE preparation. This first segment defines what a semaphore is and explains the P (wait) and V (signal) operations.

Watch from 01:55 to 04:24. Focus on the definition of a semaphore and the function of the wait (decrement) and signal (increment) operations.

To see the standard implementation of these operations, you can review the following document. It provides the pseudocode that forms the basis of how semaphores work.

Operating Systems

The document 'Operating Systems' from Vidyalankar provides the formal structure and algorithms for semaphore operations.

Read the first page of the 'SEMAPHORES' section (page 35 in the PDF). Pay attention to the struct semaphore definition and the pseudocode for the wait(semaphore s) and signal(semaphore s) functions. This gives you the 'under-the-hood' view.


2. Counting Semaphores

A counting semaphore is used to control access to a resource that has multiple instances—for example, a pool of database connections or a fixed number of identical printers.

  • Initialization: The semaphore is initialized to the number of available resources, let's say k.
  • wait() Operation: When a process wants to use a resource, it executes wait(S). This decrements the semaphore count (S--).
    • If the resulting value of S is non-negative (S >= 0), the process continues, having successfully acquired a resource.
    • If the resulting value of S is negative (S < 0), it means no resources were available. The process is blocked and placed in a waiting queue associated with the semaphore.
  • signal() Operation: When a process releases a resource, it executes signal(S). This increments the semaphore count (S++).
    • If the value of S after incrementing is less than or equal to 0 (S <= 0), it means there are processes waiting for this resource. The operating system will wake up one of the blocked processes from the queue.

A Key Insight for GATE

The value of a counting semaphore tells you about the state of the system:

  • If S > 0, there are S resources available.
  • If S = 0, all resources are currently in use, but no processes are waiting.
  • If S < 0, all resources are in use, and |S| (the absolute value of S) processes are blocked in the waiting queue.

This last point is a frequent source of GATE questions. For example, if a semaphore's value is -3, you know that 3 processes are currently blocked waiting for that resource.

Let's see this in action with an example.

GATE 2023 Exam | CSE | Operating Systems | Semaphores (Binary & Counting) | Free Online Course

The same video from BYJU's now provides a clear walkthrough of a counting semaphore example. It shows how the semaphore value changes as processes acquire and release resources.

Watch from 04:24 to 09:35. Follow the example with processes P1, P2, and P3, and observe how the semaphore value becomes negative when a process has to block.


3. Binary Semaphores

A binary semaphore is a simpler version, restricted to the values 0 and 1. It functions essentially as a mutex lock, which we discussed in the previous lesson.

  • Initialization: For mutual exclusion, a binary semaphore is initialized to 1, indicating that the critical section is initially available.
  • wait() Operation: A process calls wait(S) before entering the critical section. If S is 1, it is decremented to 0, and the process enters. If S is already 0, the process blocks until the semaphore is released.
  • signal() Operation: After leaving the critical section, the process calls signal(S). This sets S back to 1 and wakes up a waiting process, if any.

Binary semaphores are used to enforce mutual exclusion for a single resource.

GATE 2023 Exam | CSE | Operating Systems | Semaphores (Binary & Counting) | Free Online Course

This next segment from the BYJU's video explains binary semaphores and their role in providing mutual exclusion.

Watch from 09:35 to 14:09. Note how the logic ensures that only one process can be in the critical section at a time, making it behave like a mutex.

Test your understanding!

A system has 8 identical, sharable resources. A counting semaphore S is used to manage them.

  1. What should the initial value of S be?
  2. What is the value of S after 5 processes have each taken one resource?
  3. What is the value of S after 10 processes have each requested one resource? How many processes are blocked?
Show answer
  1. The initial value of S should be 8.
  2. After 5 wait() operations, the value of S will be 8 - 5 = 3.
  3. After 10 wait() operations, the value of S will be 8 - 10 = -2. This means 2 processes are blocked in the waiting queue.

4. Solving GATE Problems with Semaphores

For the GATE exam, you'll often see questions that require you to calculate the final value of a semaphore or determine system state based on a sequence of P and V operations.

Problem Type 1: Final Value Calculation

This is the most straightforward type of problem. Remember:

  • Each P operation decrements the value.
  • Each V operation increments the value.

Example 1: A counting semaphore S is initialized to 7. In a certain period, 20 P operations and 15 V operations are performed. What is the final value of S?

  • Calculation: Initial Value - (Number of P ops) + (Number of V ops)
  • 7 - 20 + 15 = -13 + 15 = 2
  • The final value is 2.

Example 2 (Slightly trickier): During an execution, 20 P operations and 12 V operations were issued in some order on a counting semaphore S. What is the largest initial value of S for which at least one P operation will remain blocked?

  • Logic: For a process to be blocked, the semaphore value must become negative. For at least one process to be blocked, the final value of the semaphore must be -1. (If it were -2, two processes would be blocked, etc.)
  • Let the initial value be S_initial.
  • Equation: S_initial - 20 + 12 = -1
  • S_initial - 8 = -1
  • S_initial = 7
  • The largest initial value is 7.

Now, watch the video instructor solve these exact problems.

GATE 2023 Exam | CSE | Operating Systems | Semaphores (Binary & Counting) | Free Online Course

This segment of the BYJU's video walks through solutions to several GATE previous year questions involving semaphore value calculations. This will solidify your understanding of the mechanics.

Watch from 14:09 to 20:27. This covers the two examples above and one more, reinforcing the problem-solving technique.

Problem Type 2: Race Conditions with Semaphores

More complex questions involve analyzing code snippets to find possible outcomes, especially when semaphores are used to control access to a shared variable. A counting semaphore initialized to a value greater than 1 does not guarantee mutual exclusion, which can lead to race conditions.

Test your understanding! (GATE 2023 Problem)

Consider two functions, incr and decr, that use a semaphore s to access a shared variable X.

// Shared variable
int X;

// Semaphore
semaphore s;

void incr() {
    wait(s);
    X = X + 1;
    signal(s);
}

void decr() {
    wait(s);
    X = X - 1;
    signal(s);
}

There are 5 threads each invoking incr once, and 3 threads each invoking decr once. The initial value of X is 10. The operations X = X + 1 and X = X - 1 are not atomic.

Consider two implementations:

  • I-1: s is a binary semaphore initialized to 1.
  • I-2: s is a counting semaphore initialized to 2.

Let V1 and V2 be the minimum possible values of X at the end of execution for I-1 and I-2, respectively. What are V1 and V2?

Show answer

Case I-1: Binary Semaphore (s = 1)

  • A binary semaphore initialized to 1 acts as a mutex. It guarantees mutual exclusion.
  • This means only one thread can be inside the critical section (X = X + 1 or X = X - 1) at any time.
  • Therefore, the operations on X are effectively serialized. The final result is deterministic, regardless of the order of execution.
  • Final Value: 10 + (5 increments) - (3 decrements) = 10 + 5 - 3 = 12.
  • So, V1 = 12.

Case I-2: Counting Semaphore (s = 2)

  • A counting semaphore initialized to 2 allows up to two threads to be in the critical section simultaneously. This breaks mutual exclusion and allows for race conditions.
  • We want to find the minimum possible value of X. This happens when decrements have the maximum impact and increments are "lost".
  • The X = X op 1 operation is a three-step process: (1) Read X into a register, (2) Perform operation in the register, (3) Write register value back to X.
  • Here is a scenario for the minimum value:
    1. An incr thread (let's call it T_inc1) enters. It executes wait(s), so s becomes 1. It reads X (value 10) into its register. It gets preempted.
    2. A decr thread (T_dec1) enters. It executes wait(s), so s becomes 0. It reads X (value 10) into its register. It also gets preempted.
    3. Now, s is 0, so no more threads can enter.
    4. Let all 4 remaining incr threads run to completion one by one. Each will wait, execute, and signal. The final value of X will be 10 + 4 = 14.
    5. Now, the original T_inc1 resumes. Its register still holds 10. It calculates 10 + 1 = 11 and writes 11 back to X. The work of the other 4 incr threads is lost! X is now 11. T_inc1 signals, so s becomes 1.
    6. Now, the original T_dec1 resumes. Its register still holds 10. It calculates 10 - 1 = 9 and writes 9 back to X. The work of all 5 incr threads is lost! X is now 9. T_dec1 signals, so s becomes 2.
    7. Finally, the remaining 2 decr threads run sequentially, decrementing X from 9 to 8, and then to 7.
  • The minimum possible value is 7.
  • So, V2 = 7.

The correct answer is V1=12, V2=7. You can find a detailed discussion of this exact problem on the Gate Overflow page for GATE CSE 2023, Question 28.


Conclusion

Today we introduced semaphores, a versatile tool that overcomes the limitations of simple mutex locks. We have seen how they can be used to manage a pool of resources or to ensure simple mutual exclusion.

Key Takeaways:

  • Semaphores are integer variables accessed only through atomic wait(P) and signal(V) operations.
  • Counting semaphores can have any non-negative integer value and are ideal for managing a pool of k identical resources. A negative value -n indicates that n processes are blocked.
  • Binary semaphores are restricted to 0 and 1 and are used for mutual exclusion, making them functionally equivalent to mutex locks.
  • Understanding the non-atomic nature of high-level programming statements (like x = x + 1) is key to solving complex GATE problems involving race conditions.

So far, we've used semaphores for basic resource counting and mutual exclusion. In our next lesson, we will apply them to solve a classic and important synchronization challenge: the Producer-Consumer Problem. This will showcase the true power of semaphores for coordinating complex process interactions.