×

Search anything:

Interprocess communication: Semaphores

Free book on Dynamic Programming

Get this book -> Problems on Array: For Interviews and Competitive Programming

In this article we discuss semaphores and how they are used for communication between processes in Linux.

Table of contents.

  1. Introduction.
  2. Allocating and deallocating semaphores.
  3. Semaphore initialization.
  4. Wait and post operations.
  5. Summary.
  6. References.

Prerequisites.

  1. Shared memory
  2. Linux threads synchronization

Introduction.

IPC(Interprocess communication) is the transfer of data between processes.

Other than semaphores we have other types of interprocess communication namely,

  • Shared memory, which allows the communication of processes by reading and writing to a specified memory location. This is discussed in the prerequisite article.
  • Mapped memory, which allow processes to communicate via a shared file in a file system.
  • Sockets, these support interprocess communication between unrelated processes even on different systems.
  • Pipes, which will allow a one sided communication between the two related processes. e.g Linux pipe command.

Semaphores are counters which allow multiple threads to synchronize, apart from synchronization semaphores, there exists an alternate implementation of semaphores referred to ad process semaphores or system V semaphores which aid in interprocess communication.
These are allocated, used then deallocated just like shared memory segments and even though one semaphore can handle these three operations, they come in sets.

Allocating and deallocating semaphores.

The semget system call is used to allocate semaphores while the semctl call deallocates them.

When we invoke semget we pass the semaphore set, the number of semaphores in the set and a permissions flag. It returns a semaphore set identifier.
For an existing semaphore we can obtain its ID by specifying the right key value.

Even after processes have terminated semaphores will continue to exist and therefore it is the responsibility of the last process to use a semaphore to remove it so as to ensure the system doesn't run out of semaphores.

The semctl call is invoked to remove semaphores. It takes the semaphore ID, the number of semaphores in the set, IPC_RMID as the third argument and a union semun value as its fourth argument.
For deallocation, the user ID of the calling process must match the semaphores allocator otherwise the caller must have root privileges.
Removing a semaphore set will lead to automatic deallocation.

An example of allocating and deallocating a binary semaphore.

#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/types.h>

// define union semun
union semun{
    int val;
    struct semid_ds *buffer;
    unsigned short int *array;
    struct seminfo *__buffer;
};

// get binary semaphore ID - allocate if necessary
int binarySemaphoreAllocation(key_t key, int semFlags){
    return semget(key, 1, semFlags);
}

// deallocate binary semaphore, return -1 on failure
int binarySemaphoreDeallocation(int semID){
    return semctl(semid, 1, IPC_RMID, ignored_argument);
}

Semaphore initialization.

The semctl call is used to initialize semaphores but in this case zero will be the second argument, SETALL the third argument and for the fourth argument we create a union semun object and point its array field at an array of unsigned short values where each value initializes a single semaphore in the set.

An example of initializing a binary semaphore,

#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/types.h>

// union semun
union semun{
    int val;
    struct semid_ds *buffer;
    unsigned short int *array;
    struct seminfo *__buffer;
};

// initialize binary semaphore with value of 1
int binarySemaphoreInit(int semID){
    union semun argument;
    unsigned short values[1];
    values[0] = 1;
    argument.array = values;
    return semctl(semid, 0, SETALL, argument);
}

Wait and post operations.

The semop system call is used to implement the wait and post operation and each semaphore will have a non-negative value used for these operations.

It takes a semaphore ID as its first parameter, an array of struct sembuf elements as its second argument and the length of this array as its third argument.

  • sembuf fields include,
  • sem_num which is the number in the semaphore set where the operation is performed.
  • sem_op which is an integer specifying the semaphore operation. If positive, it is added to the semaphore value immediately and if it is negative the absolute value of this number is subtracted from the semaphore value. When the value of the semaphore becomes negative the call blocks until it becomes as large as sem_op absolute value.
  • sem_flg which is a flag value specifying IPC_NOWAIT to prevent an operation from blocking.
  • If the operation blocks sem_op call will fail. SEM_UNDO undoes this operation on the semaphore when the process exits.

An example of wait and post operations on binary semaphore

#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/types.h>

// wait on binary semaphore
// block until value is positive, decrement by 1
int binarySemaphoreWait(int semID){
    struct sembuf operations[1];
    // use first semaphore
    operations[0].sem_num = 0;
    // decrement
    operations[0].sem_op = -1;
    // allow undo'ing
    operations[0].sem_flg = SEM_UNDO;

    return semop(semID, operations, 1);
}

// post to binary semaphore
// increment value by 1
int binarySemaphorePost(int semID){
    struct sembuf operations[1];
    // use first semaphore
    operations[0].sem_num = 0;
    // increment
    operations[0].sem_op = 1;
    // allow undo'ing
    operations[0].sem_flg = SEM_UNDO;

    return semop(semID, operations, 1);
}

SEM_UNDO flag allows dealing with the problem of a process terminating while it has allocated resources through a semaphore.

Semaphore values are automatically made to 'undo' a process' effects on the semaphore when it terminates e.g if a process that decremented a semaphore is killed, the semaphore's value is incremented.

To view information regarding semaphore sets we write,

ipcs -s

To remove a semaphore with an id 6752431 set we write,

ipcrm sem 6752431

Summary.

The semget call allocates semaphores while semctl deallocates them.
The semop call implements wait and post operations where it is required that a semaphore value be a non-negative.

A semaphore can be used for synchronizing threads or interprocess communication.

References.

  1. Execute man semget, man semctl, man semop,
  2. Mapped memory
Interprocess communication: Semaphores
Share this