POSIX Semaphore Operations Understanding sem_wait(), sem_post() and How POSIX Differs from System V

 

POSIX Semaphore Operations
Understanding sem_wait(), sem_post() and How POSIX Differs from System V
๐Ÿ“˜ Chapter 53 โ€“ TLPI
๐Ÿ”ง Linux IPC
๐ŸŽฏ Section 53.3

What Is a Semaphore Operation?

A POSIX semaphore is essentially a non-negative integer maintained by the kernel. Processes or threads control access to shared resources by decrementing (waiting) and incrementing (posting) this integer. The kernel ensures the value never goes below zero โ€” if a process tries to decrement a zero-valued semaphore, it blocks until another process increments it.

In this tutorial we cover the core operations: sem_wait(), sem_trywait(), sem_timedwait(), and sem_post(). We also explain how these differ from their System V semaphore counterparts.

Key Terms:

sem_wait() sem_post() sem_trywait() sem_timedwait() Blocking Non-blocking POSIX vs System V Critical Section semaphore.h

1. POSIX Semaphores vs System V Semaphores

Both POSIX and System V provide semaphores for process/thread synchronisation. However, there are important differences in how their operations work.

Feature POSIX Semaphores System V Semaphores
Semaphores per operation One at a time (sem_wait/sem_post) Multiple at a time (semop on a set)
Increment/Decrement amount Exactly 1 (fixed) Arbitrary value
Wait-for-zero โŒ Not available โœ… semop with sem_op=0
API complexity Simple and clean More complex, harder to use correctly
Thread support โœ… Unnamed semaphores work well with threads โš ๏ธ Primarily designed for processes
Identification Name (string) or sem_t variable Integer key and semaphore ID
๐Ÿ’ก Takeaway: POSIX semaphores appear more limited (no set operations, no wait-for-zero, fixed increment of 1), but they are sufficient for the vast majority of real-world use cases. They are also simpler to use correctly, which matters a lot in practice. Anything achievable with System V semaphores can also be done with POSIX semaphores, though sometimes with a bit more code.

2. The Semaphore Value โ€“ Understanding the Counter

A POSIX semaphore is just an integer. The kernel maintains this integer and enforces one rule: it can never go below zero.

Semaphore Value State Machine
Value = 2 Value = 1 Value = 0 sem_wait()
called here
Process
BLOCKS
sem_wait() decrements: 2โ†’1โ†’0 At 0: next sem_wait() blocks sem_post() unblocks

A semaphore with initial value 1 acts like a mutex (binary semaphore). A semaphore with initial value N acts like a counting semaphore, allowing up to N concurrent accesses to a resource.

Code: Semaphore as a Mutex (Binary Semaphore)

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>

sem_t mutex_sem;
int shared_counter = 0;

void *increment_task(void *arg)
{
    int i;
    for (i = 0; i < 100000; i++) {
        sem_wait(&mutex_sem);   /* Lock: decrement from 1 to 0 */
        shared_counter++;       /* Critical section */
        sem_post(&mutex_sem);  /* Unlock: increment from 0 to 1 */
    }
    return NULL;
}

int main(void)
{
    pthread_t t1, t2;

    /* Initialize unnamed semaphore: shared=0 (threads), initial value=1 */
    if (sem_init(&mutex_sem, 0, 1) == -1) {
        perror("sem_init");
        exit(EXIT_FAILURE);
    }

    pthread_create(&t1, NULL, increment_task, NULL);
    pthread_create(&t2, NULL, increment_task, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    printf("Final counter: %d (expected: 200000)\n", shared_counter);

    sem_destroy(&mutex_sem);
    return EXIT_SUCCESS;
}

Code: Counting Semaphore โ€“ Limit Concurrent Access

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>

#define MAX_CONCURRENT 3   /* At most 3 threads in the "pool" at once */

sem_t pool_sem;

void *worker(void *arg)
{
    int id = *(int *)arg;

    printf("Thread %d: waiting for pool slot...\n", id);
    sem_wait(&pool_sem);   /* Acquire a slot in the pool */

    printf("Thread %d: in pool, doing work\n", id);
    sleep(1);              /* Simulate work */

    printf("Thread %d: leaving pool\n", id);
    sem_post(&pool_sem);  /* Release the slot */
    return NULL;
}

int main(void)
{
    pthread_t threads[8];
    int ids[8];
    int i;

    /* Allow at most MAX_CONCURRENT threads in the "pool" */
    sem_init(&pool_sem, 0, MAX_CONCURRENT);

    for (i = 0; i < 8; i++) {
        ids[i] = i + 1;
        pthread_create(&threads[i], NULL, worker, &ids[i]);
    }
    for (i = 0; i < 8; i++)
        pthread_join(threads[i], NULL);

    sem_destroy(&pool_sem);
    printf("All threads done.\n");
    return EXIT_SUCCESS;
}

๐ŸŽฏ Interview Questions โ€“ Semaphore Operations Overview
Q1: What guarantee does the kernel provide about a semaphore’s value?

A: The kernel guarantees that a semaphore’s value will never go below zero. If a process tries to decrement a semaphore that is already at 0, the kernel blocks the calling process until another process increments the semaphore.

Q2: Can POSIX semaphore operations change the value by more than 1?

A: No. sem_wait() always decrements by exactly 1, and sem_post() always increments by exactly 1. This is unlike System V’s semop() which can add or subtract arbitrary values. If you need to change by more than 1, you must call sem_post() or sem_wait() multiple times.

Q3: What is the difference between a binary semaphore and a counting semaphore?

A: A binary semaphore has an initial value of 1 and acts like a mutex โ€” only one process/thread can hold it at a time (value oscillates between 0 and 1). A counting semaphore has an initial value of N and allows up to N concurrent holders. When value reaches 0, all further sem_wait() callers block.

Q4: Is a POSIX semaphore only a synchronisation mechanism or also a queuing mechanism?

A: It is only a synchronisation mechanism. When multiple processes are blocked on sem_wait() and a sem_post() is called, under the default round-robin scheduling policy it is indeterminate which waiting process gets unblocked. There is no FIFO queue of waiters. (Under real-time scheduling policies, the highest-priority waiter that has been waiting the longest is unblocked.)

Q5: What does incrementing a semaphore (sem_post) signify semantically?

A: Incrementing a POSIX semaphore signals that a shared resource has been released and is now available for another process or thread to use. It is the equivalent of saying “I am done with this resource, someone else can have it.”

Leave a Reply

Your email address will not be published. Required fields are marked *