Semaphore Implementation in Linux Systems

Understanding Semaphores

Semaphores serve as synchronization primitives in computer systems, providing coordination mechanisms for processes and threads in concurrent environments. They function as integer variables that regulate access to shared resources.

There are two primary semaphore types: binary semaphores and counting semaphores. Binary semaphores operate with values limited to 0 or 1, enabling mutual exclusion where only one process can access a resource at any given time. Counting semaphores can hold any non-negative integer value, allowing control over the number of concurrent resource accesses.

Semaphores support two fundamental operations: wait (P) and signal (V). The wait operation decrements the semaphore value if positive, or blocks the calling process if zero. The signal operation increments the semaphore value and potentially unblocks a waiting process.

Semaphore Operations: Wait and Signal

The core semaphore operations provide the foundation for synchronization:

  1. Wait Operation (P):
    • Processes invoke this operation before accessing protected resources
    • Decrements semaphore value if greater than zero
    • Blocks the process if semaphore value is zero
  2. Signal Operation (V):
    • Processes call this operation after completing resource usage
    • Increments semaphore value
    • Wakes up one blocked process if any are waiting

These operations execute atomically, ensuring consistent behavior in concurrent scenarios. The combination of wait and signal operations enables controlled resource access and process synchronization.

Practical Applications

Semaphores find extensive use in concurrent programming scenarios:

Mutual Exclusion Locks: Binary semaphores implement mutual exclusion by initializing the semaphore to 1. Processes acquire the lock through wait operations and release it via signal operations.

Producer-Consumer Patterns: This model utilizes two semaphores: one tracking available buffer slots and another counting filled items. Producers wait for empty slots before adding data, while consumers wait for available items before processing.

System Commands:
View semaphores: ipcs -s
Remove semaphores: ipcrm sem [semaphore_id]

Linux Semaphore Implementation

class SemaphoreController {
private:
    union sem_config {
        int initial_value;
        struct semid_ds *buffer_ptr;
        unsigned short *array_ptr;
    };
    
    int semaphore_id;
    short operation_flags;
    
    SemaphoreController(const SemaphoreController&) = delete;
    SemaphoreController& operator=(const SemaphoreController&) = delete;

public:
    SemaphoreController() : semaphore_id(-1) {}
    
    bool initialize(key_t identifier, unsigned short init_val = 1, 
                   short flags = SEM_UNDO);
    bool acquire(short operation = -1);
    bool release(short operation = 1);
    int current_value();
    bool remove();
    ~SemaphoreController();
};

bool SemaphoreController::initialize(key_t identifier, 
                                   unsigned short init_val, 
                                   short flags) {
    if (semaphore_id != -1) return false;
    
    operation_flags = flags;
    
    if ((semaphore_id = semget(identifier, 1, 0666)) == -1) {
        if (errno == ENOENT) {
            if ((semaphore_id = semget(identifier, 1, 
                                     0666 | IPC_CREAT | IPC_EXCL)) == -1) {
                if (errno == EEXIST) {
                    if ((semaphore_id = semget(identifier, 1, 0666)) == -1) {
                        perror("semget failed");
                        return false;
                    }
                    return true;
                } else {
                    perror("semget creation failed");
                    return false;
                }
            }
            
            union sem_config config;
            config.initial_value = init_val;
            if (semctl(semaphore_id, 0, SETVAL, config) < 0) {
                perror("semctl initialization failed");
                return false;
            }
        } else {
            perror("semget access failed");
            return false;
        }
    }
    return true;
}

bool SemaphoreController::acquire(short operation) {
    if (semaphore_id == -1) return false;
    
    struct sembuf operation_buffer;
    operation_buffer.sem_num = 0;
    operation_buffer.sem_op = operation;
    operation_buffer.sem_flg = operation_flags;
    
    if (semop(semaphore_id, &operation_buffer, 1) == -1) {
        perror("acquire operation failed");
        return false;
    }
    return true;
}

bool SemaphoreController::release(short operation) {
    if (semaphore_id == -1) return false;
    
    struct sembuf operation_buffer;
    operation_buffer.sem_num = 0;
    operation_buffer.sem_op = operation;
    operation_buffer.sem_flg = operation_flags;
    
    if (semop(semaphore_id, &operation_buffer, 1) == -1) {
        perror("release operation failed");
        return false;
    }
    return true;
}

int SemaphoreController::current_value() {
    return semctl(semaphore_id, 0, GETVAL);
}

bool SemaphoreController::remove() {
    if (semaphore_id == -1) return false;
    
    if (semctl(semaphore_id, 0, IPC_RMID) == -1) {
        perror("semaphore removal failed");
        return false;
    }
    return true;
}

Tags: Linux System V Semaphores Process Synchronization Concurrency Control inter-process communication

Posted on Mon, 02 Nov 2026 16:01:22 +0000 by garcon