Semaphore is a synchronization mechanism. In more words, semaphores are a technique for coordinating or synchronizing activities in which multiple processes compete for the same resources. There are 2 types of semaphores: Binary semaphores & Counting semaphores. 

  • Binary Semaphores: Only two states 0 & 1, i.e., locked/unlocked or available/unavailable, Mutex implementation.
  • Counting Semaphores: Semaphores which allow arbitrary resource count called counting semaphores.

Here, we will see the POSIX style semaphore. POSIX semaphore calls are much simpler than the System V semaphore calls. However, System V semaphores are more widely available, particularly on older Unix-like systems. POSIX semaphores have been available on Linux systems post version 2.6 that use Glibc.

There are two types of POSIX semaphores: named & unnamed. The named semaphore(which internally implemented using shared memory) generally used between processes. As it creates shared memory system-wide & can use in multiple processes. But if you have threads only then, the unnamed semaphore will be the best choice.

Semaphore between processes example in C using POSIX-semaphore

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/wait.h>

const char *semName = "asdfsd";

void parent(void){
    sem_t *sem_id = sem_open(semName, O_CREAT, 0600, 0);

    if (sem_id == SEM_FAILED){
        perror("Parent  : [sem_open] Failed\n"); return;
    }

    printf("Parent  : Wait for Child to Print\n");
    if (sem_wait(sem_id) < 0)
        printf("Parent  : [sem_wait] Failed\n");
    printf("Parent  : Child Printed! \n");
    
    if (sem_close(sem_id) != 0){
        perror("Parent  : [sem_close] Failed\n"); return;
    }

    if (sem_unlink(semName) < 0){
        printf("Parent  : [sem_unlink] Failed\n"); return;
    }
}

void child(void)
{
    sem_t *sem_id = sem_open(semName, O_CREAT, 0600, 0);

    if (sem_id == SEM_FAILED){
        perror("Child   : [sem_open] Failed\n"); return;        
    }

    printf("Child   : I am done! Release Semaphore\n");
    if (sem_post(sem_id) < 0)
        printf("Child   : [sem_post] Failed \n");
}

int main(int argc, char *argv[])
{
    pid_t pid;
    pid = fork();

    if (pid < 0){
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (!pid){
        child();
        printf("Child   : Done with sem_open \n");
    }
    else{
        int status;
        parent();
        wait(&status);
        printf("Parent  : Done with sem_open \n");
    }

    return 0;
}
  • sem_open() : Connects to, & optionally creates, a named semaphore( like sem_init()`)
  • sem_unlink() : Ends connection to an open semaphore & causes the semaphore to be removed when the last process closes it( like sem_destroy()`
  • sem_wait() Wait for the semaphore to acquire
  • sem_post() Release semaphore

General pointers

  • Semaphore’s internal implementation is like memory-mapped file(mmap)
  • Two standards of semaphore mechanism
    1. POSIX-semaphore: sem_init(), sem_destroy(), sem_wait(), sem_post(), sem_trywait(), sem_getvalue(), sem_open(), sem_unlink()
    2. System-V-semaphore: semget(), semop()semctl()