- OS - Home
- OS - Needs
- OS - Overview
- OS - History
- OS - Components
- OS - Structure
- OS - Architecture
- OS - Services
- OS - Properties
- Process Management
- Processes in Operating System
- States of a Process
- Process Schedulers
- Process Control Block
- Operations on Processes
- Inter Process Communication (IPC)
- Context Switching
- Multi-threading
- Scheduling Algorithms
- Process Scheduling
- Types of Scheduling
- Scheduling Algorithms Overview
- FCFS Scheduling Algorithm
- SJF Scheduling Algorithm
- Round Robin Scheduling Algorithm
- HRRN Scheduling Algorithm
- Priority Scheduling Algorithm
- Multilevel Queue Scheduling
- Lottery Scheduling Algorithm
- Starvation and Aging
- Turn Around Time & Waiting Time
- Burst Time in SJF Scheduling
- Process Synchronization
- Process Synchronization
- Solutions For Process Synchronization
- Hardware-Based Solution
- Software-Based Solution
- Critical Section Problem
- Critical Section Synchronization
- Mutual Exclusion Synchronization
- Peterson's Algorithm
- Dekker's Algorithm
- Bakery Algorithm
- Semaphores
- Binary Semaphores
- Counting Semaphores
- Mutex
- Turn Variable
- Bounded Buffer Problem
- Reader Writer Locks
- Test and Set Lock
- Monitors
- Sleep and Wake
- Race Condition
- OS Deadlock
- Introduction to Deadlock
- Conditions for Deadlock
- Deadlock Handling
- Deadlock Prevention
- Deadlock Avoidance (Banker's Algorithm)
- Deadlock Detection and Recovery
- Deadlock Ignorance
- Memory Management
- Memory Management
- Contiguous Memory Allocation
- Non-Contiguous Memory Allocation
- First Fit Algorithm
- Next Fit Algorithm
- Best Fit Algorithm
- Worst Fit Algorithm
- Fragmentation
- Virtual Memory
- Segmentation
- Buddy System
- Slab Allocation
- Overlays
- Paging and Page Replacement
- Paging
- Demand Paging
- Page Table
- Page Replacement Algorithms
- Optimal Page Replacement Algorithm
- Belady's Anomaly
- Thrashing
- Storage and File Management
- File Systems
- File Attributes
- Structures of Directory
- Linked Index Allocation
- Indexed Allocation
- Disk Scheduling Algorithms
- FCFS Disk Scheduling
- SSTF Disk Scheduling
- SCAN Disk Scheduling
- LOOK Disk Scheduling
- I/O Systems
- I/O Hardware
- I/O Software
- OS Types
- OS - Types
- OS - Batch Processing
- OS - Multiprocessing
- OS - Hybrid
- OS - Monolithic
- OS - Zephyr
- OS - Nix
- OS - Linux
- OS - Blackberry
- OS - Garuda
- OS - Tails
- OS - Clustered
- OS - Haiku
- OS - AIX
- OS - Solus
- OS - Tizen
- OS - Bharat
- OS - Fire
- OS - Bliss
- OS - VxWorks
- OS - Embedded
- OS - Single User
- Miscellaneous Topics
- OS - Security
- OS Questions Answers
- OS - Questions Answers
- OS Useful Resources
- OS - Quick Guide
- OS - Useful Resources
- OS - Discussion
Operating System - Binary Semaphores
Semaphores are two types. Counting semaphores and binary semaphores. A binary semaphore is a special type of semaphore that ensures mutual exclusion for a single resource between multiple processes. Read this chapter to get clear understanding of binary semaphores, their operations, working, and implementation in operating systems.
- What is Binary Semaphores?
- Working of Binary Semaphore
- Implementation of Binary Semaphore
- Binary Semaphore vs Counting Semaphore
What is Binary Semaphores?
A binary semaphore is a synchronization mechanism used in operating systems to manage access to shared resources. It can have only two states: wait (S) and signal (S). The wait operation decreases the semaphore value to 0, this indicates that the resource is being used. The signal operation increases the semaphore value to 1, indicating that the resource is available. So, a process can enter its critical section only when the semaphore value is 1.
// Wait operation
wait(S) {
while (S <= 0);
S--;
}
// Signal operation
signal(S) {
S++;
}
Now, let's understand the working of binary semaphores with an example.
Working of Binary Semaphore
To understand working of semaphores, take a situation between two processes, P1 and P2, that need to access a shared resource. Initially, the semaphore S is set to 1, this indicates that the resource is available.
State 1 − Both P1 and P2 are in their non-critical sections.
State 2 − P1 wants to enter its critical section, so it performs wait(S). So S is decremented to 0.
State 3 − P1 is in critical section, now P2 also wants to enter its critical section, so it performs wait(S). Since S is 0, P2 is blocked and waits.
State 4 − P1 finishes its critical section and performs signal(S). So S is incremented to 1.
State 5 − P2 is unblocked and performs wait(S). So S is decremented to 0, and P2 enters its critical section.
State 6 − P2 finishes its critical section and performs signal(S). So S is incremented to 1.
The following diagram illustrates the above steps −
Implementation of Binary Semaphore
Binary semaphores can be implemented using various methods in operating systems. One common approach is to use atomic operations to ensure that the wait and signal operations are executed without interruption. Here is a simple implementation of binary semaphores in CPP / Python / Java −
import threading
class BinarySemaphore:
def __init__(self):
self.semaphore = threading.Semaphore(1)
def wait(self):
self.semaphore.acquire()
def signal(self):
self.semaphore.release()
# Example usage
binary_semaphore = BinarySemaphore()
def process(name):
print(f"{name} is trying to enter critical section")
binary_semaphore.wait()
print(f"{name} has entered critical section")
# Critical section
binary_semaphore.signal()
print(f"{name} has exited critical section")
thread1 = threading.Thread(target=process, args=("Process 1",))
thread2 = threading.Thread(target=process, args=("Process 2",))
thread1.start()
thread2.start()
The output of the above code will be −
Process 1 is trying to enter critical section Process 1 has entered critical section Process 1 has exited critical section Process 2 is trying to enter critical section Process 2 has entered critical section Process 2 has exited critical section
#include <iostream>
#include <thread>
#include <mutex>
class BinarySemaphore {
private:
std::mutex mtx;
bool available;
public:
BinarySemaphore() : available(true) {}
void wait() {
std::unique_lock<std::mutex> lock(mtx);
while (!available);
available = false;
}
void signal() {
std::unique_lock<std::mutex> lock(mtx);
available = true;
}
};
// Example usage
BinarySemaphore binary_semaphore;
void process(const std::string &name) {
std::cout << name << " is trying to enter critical section" << std::endl;
binary_semaphore.wait();
std::cout << name << " has entered critical section" << std::endl;
// Critical section
binary_semaphore.signal();
std::cout << name << " has exited critical section" << std::endl;
}
int main() {
std::thread thread1(process, "Process 1");
std::thread thread2(process, "Process 2");
thread1.join();
thread2.join();
return 0;
}
The output of the above code will be −
Process 1 is trying to enter critical section Process 1 has entered critical section Process 1 has exited critical section Process 2 is trying to enter critical section Process 2 has entered critical section Process 2 has exited critical section
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
public static void main(String[] args) {
Thread thread1 = new Thread(() -> Main.process("Process 1"));
Thread thread2 = new Thread(() -> Main.process("Process 2"));
thread1.start();
thread2.start();
}
static class BinarySemaphore {
private final Semaphore semaphore;
public BinarySemaphore() {
semaphore = new Semaphore(1);
}
public void waitSemaphore() throws InterruptedException {
semaphore.acquire();
}
public void signal() {
semaphore.release();
}
}
class Main {
private static final BinarySemaphore binarySemaphore = new BinarySemaphore();
public static void process(String name) {
try {
System.out.println(name + " is trying to enter critical section");
binarySemaphore.waitSemaphore();
System.out.println(name + " has entered critical section");
// Critical section
binarySemaphore.signal();
System.out.println(name + " has exited critical section");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
The output of the above code will be −
Process 1 is trying to enter critical section Process 1 has entered critical section Process 1 has exited critical section Process 2 is trying to enter critical section Process 2 has entered critical section Process 2 has exited critical section
Binary Semaphore vs Counting Semaphore
Counting semaphores are advanced version of binary semaphores, which can be used to manage access to multiple instances of a resource. Here we tabulated the differences between binary semaphore and counting semaphore −
| Aspect | Binary Semaphore | Counting Semaphore |
|---|---|---|
| Definition | A semaphore that can take only the values 0 and 1 | A semaphore that can take non-negative integer values |
| Value Range | 0 or 1 | 0 to N (where N is a positive integer) |
| Use Case | Mutual exclusion for a single resource | Managing access to multiple instances of a resource |
| Operations | wait() and signal() | wait() and signal() |
| Blocking Behavior | Blocks if the resource is unavailable (value is 0) | Blocks if no resources are available (value is 0) |
Conclusion
Binary semaphores are essential synchronization tools in operating systems that have single instances of resources. They help prevent race conditions and ensure mutual exclusion in critical sections. Understanding their implementation and working is crucial for designing robust concurrent systems.