What is a Named Semaphore?
A named semaphore has a name that looks like a filesystem path, starting with a forward slash (e.g., /mysemaphore). Any process on the system can open it by name — even unrelated processes that were not forked from a common parent. Named semaphores exist in the kernel until they are explicitly deleted with sem_unlink(), even after all processes close them.
On Linux, named semaphores are stored under /dev/shm/ as files with the prefix sem.. For example, /myapp becomes /dev/shm/sem.myapp.
Note: sem_close() and sem_unlink() are separate steps — just like close() and unlink() for files. A process must call sem_close() to free its descriptor. The semaphore itself is only removed from the system after sem_unlink() is called AND all processes have closed it.
sem_open() is used to create a new named semaphore or open an existing one.
#include <semaphore.h>
#include <fcntl.h>
/* Open an existing semaphore */
sem_t *sem_open(const char *name, int oflag);
/* Create a new semaphore */
sem_t *sem_open(const char *name, int oflag,
mode_t mode, unsigned int value);
/* Returns: pointer to sem_t on success, SEM_FAILED on error */
| Parameter | Description |
|---|---|
name |
Name starting with /. Only one / allowed. Example: “/myapp_sem” |
oflag |
O_CREAT to create. O_CREAT | O_EXCL to fail if already exists. 0 to just open. |
mode |
Permission bits (like file mode). Example: 0660. Masked by process umask. |
value |
Initial value of the semaphore (e.g., 1 for binary, N for pool of N resources). |
Important: If O_CREAT is given and the semaphore already exists, the existing semaphore is opened and the mode/value arguments are ignored.
#include <semaphore.h>
int sem_close(sem_t *sem);
/* Returns 0 on success, -1 on error */
int sem_unlink(const char *name);
/* Returns 0 on success, -1 on error */
Closes the calling process’s association with a named semaphore and deallocates the resources the process was using for the semaphore. Does NOT delete the semaphore itself. Other processes can still use it. A semaphore is also automatically closed when a process terminates or calls exec().
Removes the semaphore name from the system. The semaphore itself is destroyed only after all processes that have it open call sem_close() (or terminate). This is like unlink() for files — deletion is deferred until no open references remain.
#include <semaphore.h>
int sem_wait(sem_t *sem);
/* Blocks if value == 0. Decrements value. Returns 0 or -1 */
int sem_trywait(sem_t *sem);
/* Non-blocking: fails with EAGAIN if value == 0 */
#include <time.h>
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
/* Blocks with a timeout. Fails with ETIMEDOUT if time expires */
| Function | When sem == 0 | Use Case |
|---|---|---|
sem_wait() |
Blocks indefinitely | Normal producer-consumer sync |
sem_trywait() |
Returns -1, errno=EAGAIN | Polling, when blocking is not acceptable |
sem_timedwait() |
Blocks until timeout, then -1, errno=ETIMEDOUT | Real-time systems, avoiding deadlocks |
Note: sem_wait() is interruptible. If a signal is received while waiting, it returns -1 with errno=EINTR. Always handle this with a loop or use SA_RESTART.
#include <semaphore.h>
int sem_post(sem_t *sem);
/* Returns 0 on success, -1 on error */
Increments the semaphore value by 1. If any process/thread was blocked in sem_wait(), exactly one is woken up. sem_post() is async-signal-safe, meaning it can safely be called from within a signal handler — something you cannot do with mutex operations.
#include <semaphore.h>
int sem_getvalue(sem_t *sem, int *sval);
/* Returns 0 on success, -1 on error */
/* *sval is set to current value */
Reads the current value of the semaphore into *sval. On Linux, if processes are blocked waiting, *sval is set to 0 (some other implementations return a negative number indicating the count of waiters). Use with caution: by the time you read the value, another thread may have already changed it.
This example shows a producer process creating a named semaphore and a consumer process waiting on it. Run the producer first, then the consumer.
/* producer.c
* Creates a named semaphore and signals the consumer.
* Compile: gcc -o producer producer.c -lpthread
* Run: ./producer
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>
#define SEM_NAME "/ep_demo_sem"
int main(void) {
sem_t *sem;
/* Create semaphore: initial value = 0 (consumer will block) */
sem = sem_open(SEM_NAME, O_CREAT | O_EXCL, 0660, 0);
if (sem == SEM_FAILED) {
perror("sem_open");
exit(EXIT_FAILURE);
}
printf("Producer: semaphore created, doing some work...\n");
sleep(2); /* Simulate work */
printf("Producer: work done, signaling consumer\n");
if (sem_post(sem) == -1) {
perror("sem_post");
exit(EXIT_FAILURE);
}
sem_close(sem);
/* Note: we don't unlink here — let consumer clean up */
return 0;
}
/* consumer.c
* Opens existing named semaphore and waits for producer signal.
* Compile: gcc -o consumer consumer.c -lpthread
* Run: ./consumer (run after producer)
*/
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <fcntl.h>
#define SEM_NAME "/ep_demo_sem"
int main(void) {
sem_t *sem;
/* Open existing semaphore (must already be created by producer) */
sem = sem_open(SEM_NAME, 0);
if (sem == SEM_FAILED) {
perror("sem_open");
exit(EXIT_FAILURE);
}
printf("Consumer: waiting for producer signal...\n");
/* Block until producer calls sem_post() */
if (sem_wait(sem) == -1) {
perror("sem_wait");
exit(EXIT_FAILURE);
}
printf("Consumer: got signal, proceeding with work!\n");
sem_close(sem);
sem_unlink(SEM_NAME); /* Clean up: remove semaphore from system */
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <semaphore.h>
#include <fcntl.h>
#define SEM_NAME "/ep_timed_sem"
#define TIMEOUT_SECONDS 3
int main(void) {
sem_t *sem;
struct timespec ts;
sem = sem_open(SEM_NAME, O_CREAT, 0660, 0);
if (sem == SEM_FAILED) {
perror("sem_open");
exit(EXIT_FAILURE);
}
/* Set absolute timeout: current time + 3 seconds */
if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
perror("clock_gettime");
exit(EXIT_FAILURE);
}
ts.tv_sec += TIMEOUT_SECONDS;
printf("Waiting up to %d seconds...\n", TIMEOUT_SECONDS);
if (sem_timedwait(sem, &ts) == -1) {
if (errno == ETIMEDOUT) {
printf("Timed out! No signal received.\n");
} else {
perror("sem_timedwait");
}
} else {
printf("Semaphore acquired successfully!\n");
}
sem_close(sem);
sem_unlink(SEM_NAME);
return 0;
}
/* Compile: gcc -o timed_wait timed_wait.c -lpthread -lrt */
/* Resource pool using counting semaphore.
* Pool has 3 connections available.
*/
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <fcntl.h>
#define SEM_NAME "/ep_pool_sem"
#define POOL_SIZE 3
int main(void) {
sem_t *sem;
int val, i;
/* Create semaphore with value = pool size */
sem = sem_open(SEM_NAME, O_CREAT | O_EXCL, 0660, POOL_SIZE);
if (sem == SEM_FAILED) {
perror("sem_open");
exit(EXIT_FAILURE);
}
/* Acquire connections (non-blocking) */
for (i = 0; i < 5; i++) {
if (sem_trywait(sem) == 0) {
sem_getvalue(sem, &val);
printf("Got connection #%d. Remaining: %d\n", i+1, val);
} else {
printf("Request #%d: No connection available (pool full)\n", i+1);
}
}
/* Release 2 connections back */
printf("\nReleasing 2 connections...\n");
sem_post(sem);
sem_post(sem);
sem_getvalue(sem, &val);
printf("Pool value after release: %d\n", val);
sem_close(sem);
sem_unlink(SEM_NAME);
return 0;
}
Example: “/mysem”, not “mysem”
/sem/name is not portable. Use /semname.
NAME_MAX – 4 characters (Linux: 251 chars)
Never copy the sem_t returned by sem_open. Always use the original pointer.
O_CREAT: create semaphore if it doesn’t exist. O_EXCL: with O_CREAT, fail if semaphore already exists (useful for ensuring only one creator). 0: just open an existing semaphore without creating.
sem_close() terminates the calling process’s association with the semaphore (like close() for a file descriptor) but the semaphore remains in the system. sem_unlink() removes the semaphore’s name so no new processes can open it, and the semaphore is destroyed once all current users close it.
On Linux, named semaphores are stored as files in /dev/shm/ with the prefix sem.. So sem_open("/myapp", ...) creates /dev/shm/sem.myapp. This is a tmpfs filesystem and lives in RAM.
sem_post() is listed in POSIX’s table of async-signal-safe functions (Table 21-1 in TLPI), meaning it can be safely called from inside a signal handler. This is useful when you want a signal handler to wake up a blocked thread. Mutex functions are NOT async-signal-safe and cannot be used in signal handlers.
A named semaphore persists in the system (in /dev/shm) even after all processes using it crash or exit without cleanup. On reboot it disappears (since /dev/shm is in RAM). This can cause problems at restart — next time the program runs, sem_open() with O_CREAT | O_EXCL will fail because the old semaphore file still exists.
It returns -1 and sets errno to EAGAIN (resource temporarily unavailable). This allows the caller to take an alternative action rather than blocking.
Next: Unnamed Semaphores
Learn about sem_init, sem_destroy, thread-sharing, and process-sharing via shared memory.
