Named Semaphores POSIX Semaphores

 

Named Semaphores
POSIX Semaphores Chapter 53 — File 2 of 5

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.

Named Semaphore Lifecycle
sem_open()
Create or open
sem_wait() / sem_post()
Use the semaphore
sem_close()
Close descriptor
sem_unlink()
Delete from system

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() — Create or Open a Named Semaphore

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 */
Parameters Explained:
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.

sem_close() and sem_unlink()
#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 */
sem_close()

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().

sem_unlink()

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.

sem_wait(), sem_trywait(), sem_timedwait() — Decrement (Acquire)
#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.

sem_post() — Increment (Release)
#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.

sem_getvalue() — Read Current Value
#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.

Complete Example: Named Semaphore Between Two Processes

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 semaphore and posts it
/* 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 — Waits on semaphore
/* 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;
}

Example: sem_timedwait() — Wait with Timeout
#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 */

Example: sem_trywait() and sem_getvalue() for Resource Pool
/* 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;
}

Important Rules for Named Semaphore Names
✓ Must start with /

Example: “/mysem”, not “mysem”

✓ Only one slash

/sem/name is not portable. Use /semname.

✓ Maximum length

NAME_MAX – 4 characters (Linux: 251 chars)

✗ Don’t operate on copies

Never copy the sem_t returned by sem_open. Always use the original pointer.

Interview Questions — Named Semaphores
Q1. What flags can be passed to sem_open()?

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.

Q2. What is the difference between sem_close() and sem_unlink()?

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.

Q3. Where are named semaphores stored on Linux?

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.

Q4. Why is sem_post() considered async-signal-safe?

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.

Q5. What happens if a process crashes without calling sem_close() or sem_unlink()?

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.

Q6. What error does sem_trywait() return when the semaphore value is 0?

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.

← Overview File 3: Unnamed Semaphores →

Leave a Reply

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