What are Linux Semaphores?

Learn via video courses
Topics Covered

Linux semaphores are synchronization mechanisms used to control access to shared resources in multi-threaded or multi-process environments.

Race Condition:

A race condition occurs when multiple threads or processes try to access shared resources or perform critical operations simultaneously without proper synchronization.

To understand race conditions better, let's consider a simple example:

Imagine a bank account with a balance of ₹100 and two processes(A and B) that want to deposit money concurrently. Consider the following order of execution,

  • Process A gets balance (₹100) and deposit.
  • Now, Process B reads the current balance(₹100).
  • Process A writes the new balance(₹150) into the balance variable.
  • Process B writes the new balance(₹170) into the balance variable.

The final value depends on the order of execution of the processes. If Process A executes first, the final balance will be ₹150, otherwise it will be ₹170.

This unpredictability is the race condition and Semaphores plays a vital role in preventing conflicts in concurrent programs.

Fix with Semaphore:

A semaphore acts as a lock to ensure that only one process can access the bank balance at a time, in the following way:

  • When Process A accesses the balance and if the semaphore value is 1, it decrements the semaphore's value to 0 (locking the semaphore) and updates the balance.
  • Since the semaphore is locked (value = 0), Process B cannot access the balance and will be put into a waiting state.
  • Once Process A completes its task, it increments the semaphore's value to 1 (unlocking the semaphore).
  • Process B can proceed to access the balance as the semaphore is unlocked.

Types of Semaphores:

There are two types of Semaphore in Linux:

  • Binary semaphores
  • Counting semaphores

Binary semaphores:
Binary semaphores are simple, representing only two states - locked(0) and unlocked(1). They are primarily used to achieve mutual exclusion, allowing only one process to access a shared resource at a time. The example in the last section uses a binary semaphore.

Counting Semaphores:
Counting semaphores can take non-negative integer values and are used to control access to multiple instances of shared resources. They enable a specified number of processes to access a resource simultaneously.

Comparision between Binary and Counting Semaphores:

FeatureBinary SemaphoreCounting Semaphore
Number of StatesTwo states: Locked (0) and Unlocked (1)Multiple states (non-negative integer values)
UsageUsed for mutual exclusionUsed for resource allocation and control
Resource OwnershipExclusive ownership by one processMultiple processes can share the resource
Initial ValueTypically initialized to 1Initialized to the available resource count
Value ManipulationLock and unlock operationsIncrement and decrement operations
ExampleControlling access to a critical sectionManaging access to a limited pool of resources

Implementation of Semaphores in Linux:

Linux provides a Semaphore API, which has a set of functions that allow developers to work with semaphores in linux efficiently with their programs. Let us explore the key functions of the API,

The semget() function is used to create a new semaphore or get an existing semaphore's identifier.

Syntax:

It takes three arguments:

  • key:
    Used to identify the semaphore set. It can be either IPC_PRIVATE (to create a new semaphore set) or an identifier used to get an existing semaphore set.
  • num_sems:
    Number of semaphores to create in the semaphore set. Typically, this is set to 1 for single semaphores.
  • sem_flags:
    Permission flags for the semaphore linux set, specified as an octal number. It determines the read, write, and execute permissions for the semaphore set.

It returns the linux semaphore identifier (semid) if successful or -1 on failure.

The semctl() function is used to control semaphore behavior.

Syntax:

It takes three arguments:

  • semid:
    Semaphore linux identifier representing the semaphore set on which the operation will be performed.
  • sem_num:
    Semaphore number within the semaphore set on which the operation will be performed. For single semaphores, this is usually set to 0.
  • command:
    Specifies the operation to be performed on the Semaphore linux set. The most used values are:
    • IPC_STAT:
      Get the semaphore set status and store it in a struct semid_ds.
    • IPC_SET:
      Set the semaphore set status using the information from a struct semid_ds.
    • IPC_RMID:
      Remove the semaphore set from the system.
    • GETVAL:
      Get the value of the semaphore.
    • GETPID:
      Get the process ID of the last operation that modified the semaphore.
  • Optional Argument:
    Depending on the command used, there might be an optional fourth argument, which is a pointer to a union semun. The union semun allows specifying additional parameters for some commands, such as the new value for SETVAL or an array of semaphore values for SETALL.

This function returns the result of the specified command, depending on the operation performed. On failure, it returns -1 to indicate an error.

The semop() function is used to perform operations, such as locking and unlocking semaphores in linux.

Syntax:

It takes three arguments:

  • semid:
    The linux semaphore identifier.
  • sops:
    Pointer to an array of struct sembuf, specifying the Semaphore linux operations to be performed.
  • num_sops:
    Number of elements in the sops array, indicating the number of semaphore operations to be performed.

The struct sembuf data structure contains three fields:

  • sem_num:
    Semaphore number within the semaphore set. For single semaphores, this is usually set to 0.
  • sem_op:
    The operation to be performed on the semaphore, which can be a positive or negative value.
  • sem_flg:
    Flags that control the behavior of the operation, such as SEM_UNDO (automatic semaphore release on process termination) or IPC_NOWAIT (return immediately if the semaphore is not available).

This function returns 0 if the semaphore operations are successful or -1, on error.

Examples:

Binary Semaphore:

Explanation:

  • The Semaphore linux related functions are present in the sys/sem.h header file.
  • We create a new binary semaphore in linux by using the semget function. The argument 0666 | IPC_CREAT sets the read and write access permission flags for the semaphore.
  • The sem_num field of the sem_op structure is set to 0. This field specifies the Linux semaphore number within the semaphore set (single semaphore in this case) on which the operation will be performed.
  • The sem_op field of the sem_op structure is set to 1, indicating the operation to be performed, which is to increment the semaphore value, effectively unlocking it.

Counting Semaphore

Explanation:

  • Create a new single linux semaphore set and set the sem_num field of the sem_op structure to 0. Since there is only one semaphore in the set, the operation will be performed on the first (and only) semaphore.
  • The sem_op field of the sem_op structure is set to 2 for incrementing the semaphore value by 2.
  • The line sem_op.sem_flg = 0; indicates that no special flags are specified for this operation.
  • The semctl function with the IPC_RMID argument deletes the linux semaphore set from the system.

Conclusion

  • Linux semaphores are used to manage shared resources and prevent race conditions in multi-process environments.
  • There are two types of semaphore in Linux: Binary Semaphores and Counting Semaphores.
  • Binary Semaphores have two states (locked and unlocked) and are primarily used for mutual exclusion, allowing only one process to access a shared resource at a time.
  • Counting Semaphores can take non-negative integer values and are used to control access to a pool of identical resources, allowing a specified number of processes to access them concurrently.
  • The Semaphore linux API provides functions to create, manage, and manipulate semaphores, including semget, semctl, and semop.