What are POSIX Semaphores?
A semaphore is a kernel-maintained integer whose value is never allowed to fall below zero. It is used to control access to shared resources among multiple processes or threads. POSIX semaphores are the modern, simpler alternative to the older System V semaphore API.
Think of a semaphore like a token counter at a parking lot. If tokens are available (value > 0), a car can enter (decrement). When the lot is full (value = 0), cars wait. When a car leaves, it returns the token (increment).
POSIX provides two types of semaphores — Named and Unnamed. Both use the same underlying operations but differ in how they are created and shared.
Key Terms in This Chapter
The table below summarizes the two types of POSIX semaphores side by side.
| Property | Named Semaphore | Unnamed Semaphore |
|---|---|---|
| How created | sem_open() with a name like /mysem | sem_init() on a sem_t variable |
| Identified by | A filesystem-like name (string) | A memory address (pointer) |
| Sharing | Any process knowing the name | Threads in same process OR processes sharing memory |
| Persistent | Yes, until sem_unlink() called | Exists only as long as memory lives |
| Cleanup | sem_close() + sem_unlink() | sem_destroy() |
| Use case | Cross-process synchronization | Thread sync or process sync via shared memory |
A semaphore holds a non-negative integer value. Two fundamental operations exist:
If semaphore value > 0, decrement it and return immediately. If value == 0, block (sleep) until another thread/process increments it. Also called “down”, “acquire”, or “lock”.
Always increments the semaphore value by 1. If any process/thread was blocked waiting, one is woken up. Also called “up”, “release”, or “signal”. This operation never blocks.
Value is either 0 or 1. Used like a mutex to protect a critical section. Initialized to 1. One thread/process “acquires” it (sets to 0), does work, then “releases” it (sets to 1).
Value can be any non-negative integer. Used to control access to a pool of resources (e.g., 5 database connections). Initialized to N = number of available resources.
Below is a minimal example showing the concept of protecting shared data using a POSIX semaphore. Detailed examples are in the Named and Unnamed tutorial files.
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
/*
* Conceptual example: binary semaphore as mutex
* sem initialized to 1 = "unlocked"
* sem_wait() = acquire lock (value 1 -> 0)
* sem_post() = release lock (value 0 -> 1)
*/
sem_t sem;
int shared_counter = 0;
void safe_increment(void) {
/* Acquire: blocks if another thread holds the lock */
if (sem_wait(&sem) == -1) {
perror("sem_wait");
exit(EXIT_FAILURE);
}
/* --- Critical Section --- */
shared_counter++;
printf("Counter: %d\n", shared_counter);
/* --- End Critical Section --- */
/* Release: allow other threads to proceed */
if (sem_post(&sem) == -1) {
perror("sem_post");
exit(EXIT_FAILURE);
}
}
int main(void) {
/* Initialize unnamed semaphore: thread-shared, initial value = 1 */
if (sem_init(&sem, 0, 1) == -1) {
perror("sem_init");
exit(EXIT_FAILURE);
}
safe_increment();
safe_increment();
sem_destroy(&sem);
return 0;
}
Compile: gcc -o sem_basic sem_basic.c -lpthread — Always link with -lpthread (or -lrt on older systems) when using POSIX semaphores.
A semaphore is a kernel-maintained integer used for synchronization. It can have values > 1 (counting semaphore), allowing it to control access to a pool of resources. A mutex is strictly binary (locked/unlocked) and has ownership — only the thread that locked it can unlock it. A semaphore has no ownership; any thread/process can post it.
Named semaphores are identified by a string name (like /mysem) and can be shared between unrelated processes. Unnamed semaphores reside in memory (a sem_t variable) and are shared either between threads of the same process or between related processes via shared memory.
No. The semaphore value is always ≥ 0. When a process calls sem_wait() on a zero-valued semaphore, it blocks instead of making the value negative. This blocking behavior is what makes semaphores useful for synchronization.
<semaphore.h> is required. When compiling, you must also link with -lpthread. On older Linux systems, you may also need -lrt for real-time extensions.
P (proberen / wait / down): Tries to decrement the semaphore. Blocks if value is 0. Corresponds to sem_wait(). V (verhogen / post / up): Increments the semaphore. Wakes a waiting process if any. Corresponds to sem_post(). These Dutch terms come from Dijkstra who invented semaphores.
Start Learning POSIX Semaphores
Continue to the next file to learn Named Semaphores in depth.
