What Are Unnamed Semaphores?
Unnamed semaphores (also called memory-based semaphores) are sem_t variables that exist in memory rather than having a filesystem name. They have no path in /dev/shm/ and cannot be opened by name from another process.
To share an unnamed semaphore between threads: place it in a global variable or heap memory. To share it between processes: place it in a POSIX shared memory object or a memory-mapped file.
sem_init() – Initialize an Unnamed Semaphore
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
/* Returns: 0 on success, -1 on error */
| Parameter | Type | Meaning |
|---|---|---|
sem |
sem_t * |
Pointer to the sem_t variable to initialize |
pshared |
int |
0 = shared between threads of same process Non-zero = shared between processes (must be in shared memory) |
value |
unsigned int |
Initial value of the semaphore (0 to SEM_VALUE_MAX) |
The pshared Flag — Thread vs Process Sharing
The semaphore is shared between threads within the same process.
Location: global variable or heap (malloc()).
sem_t g_sem; /* global */
sem_init(&g_sem, 0, 1);
The semaphore is shared between different processes.
Location: POSIX shared memory or mmap’d file — processes must all map the same memory region.
sem_t *sp = mmap(...);
sem_init(sp, 1, 1);
sem_destroy() – Destroy an Unnamed Semaphore
#include <semaphore.h>
int sem_destroy(sem_t *sem);
/* Returns: 0 on success, -1 on error */
sem_destroy() releases any resources associated with the semaphore. After this call, the sem_t variable must not be used unless re-initialized.
Destroying a semaphore that has waiting threads results in undefined behavior. Always ensure no thread is blocked in sem_wait() before calling sem_destroy().
Unnamed Semaphore Lifecycle
Example 1: Thread Synchronization (pshared=0)
A background worker thread signals the main thread when its job is done.
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
sem_t done_sem; /* unnamed, thread-shared (pshared=0) */
void *worker(void *arg)
{
printf("Worker: starting task...\n");
sleep(2); /* simulate work */
printf("Worker: task complete.\n");
sem_post(&done_sem); /* signal main thread */
return NULL;
}
int main(void)
{
pthread_t tid;
sem_init(&done_sem, 0, 0); /* start at 0: main will block */
pthread_create(&tid, NULL, worker, NULL);
printf("Main: waiting for worker...\n");
sem_wait(&done_sem); /* block until worker posts */
printf("Main: worker is done, continuing.\n");
pthread_join(tid, NULL);
sem_destroy(&done_sem);
return 0;
}
Example 2: Process Sharing via mmap (pshared=1)
For sharing between unrelated processes, the semaphore must live in shared memory. Here we use anonymous mmap() and fork().
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <semaphore.h>
#include <unistd.h>
int main(void)
{
/* Allocate shared memory visible to parent and child */
sem_t *sem = mmap(NULL, sizeof(sem_t),
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (sem == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
/* pshared = 1: process sharing */
sem_init(sem, 1, 0);
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) {
/* Child: do work then signal parent */
printf("Child (pid=%d): doing work...\n", getpid());
sleep(2);
printf("Child: posting semaphore.\n");
sem_post(sem);
exit(EXIT_SUCCESS);
} else {
/* Parent: wait for child's signal */
printf("Parent: waiting for child...\n");
sem_wait(sem);
printf("Parent: received signal from child.\n");
wait(NULL);
}
sem_destroy(sem);
munmap(sem, sizeof(sem_t));
return 0;
}
gcc process_sem.c -o process_sem -lpthread
./process_sem
Example 3: Unnamed Semaphore in POSIX Shared Memory
For truly unrelated processes (no fork relationship), place the unnamed semaphore inside a POSIX shared memory object.
shm_writer.c – Creates shared memory and semaphore
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <unistd.h>
#define SHM_NAME "/ep_shm_demo"
/* Layout of the shared memory region */
struct shared_data {
sem_t sem;
int value;
};
int main(void)
{
/* Create and size the shared memory object */
int fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
ftruncate(fd, sizeof(struct shared_data));
struct shared_data *shm = mmap(NULL, sizeof(struct shared_data),
PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
close(fd);
/* Initialize the semaphore inside shared memory, pshared=1 */
sem_init(&shm->sem, 1, 0);
shm->value = 42;
printf("Writer: wrote value=%d, posting semaphore.\n", shm->value);
sem_post(&shm->sem);
sleep(5); /* keep shm alive for reader */
sem_destroy(&shm->sem);
munmap(shm, sizeof(struct shared_data));
shm_unlink(SHM_NAME);
return 0;
}
shm_reader.c – Opens shared memory and waits on semaphore
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <unistd.h>
#define SHM_NAME "/ep_shm_demo"
struct shared_data {
sem_t sem;
int value;
};
int main(void)
{
int fd = shm_open(SHM_NAME, O_RDWR, 0);
struct shared_data *shm = mmap(NULL, sizeof(struct shared_data),
PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
close(fd);
printf("Reader: waiting for semaphore...\n");
sem_wait(&shm->sem);
printf("Reader: got semaphore, value = %d\n", shm->value);
munmap(shm, sizeof(struct shared_data));
return 0;
}
Common Pitfalls with Unnamed Semaphores
| Mistake | Result | Fix |
|---|---|---|
| pshared=1 but sem_t is on the stack | Undefined behavior — child gets a copy, not the same object | Put the sem_t in mmap’d or shm_open’d memory |
| Copying sem_t with struct copy or memcpy | The copy is unusable — opaque type, do not copy | Always pass a pointer to the original sem_t |
| Calling sem_destroy while threads are waiting | Undefined behavior, crash, or deadlock | Join all threads before sem_destroy |
| Forgetting sem_init() before first use | Undefined behavior (uninitialized memory) | Always call sem_init before any operation |
