Named Semaphores sem_open, sem_close, sem_unlink

 

Named Semaphores
Chapter 53 – Part 2 of 6 | sem_open, sem_close, sem_unlink

What Are Named Semaphores?

Named semaphores are identified by a name in the filesystem. Any unrelated process that knows the name and has the right permissions can open and use the semaphore. This makes named semaphores the right choice for inter-process synchronization where the processes are not related by fork.

The lifecycle of a named semaphore is: create/open → use → close → unlink.

sem_open() – Create or Open a Named Semaphore

#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>

sem_t *sem_open(const char *name, int oflag, ...
                /* mode_t mode, unsigned int value */ );
/* Returns: pointer to semaphore on success, SEM_FAILED on error */

sem_open() either creates a new semaphore or opens an existing one, depending on the flags passed.

Parameter Description
name Semaphore name. Must start with /. No further slashes. Example: "/counter"
oflag O_CREAT to create if not exists. O_CREAT | O_EXCL to fail if it already exists. 0 to open existing only.
mode Permission bits (like file permissions, e.g. 0600). Only used when O_CREAT is set.
value Initial value of the semaphore. Only used when O_CREAT is set and the semaphore is actually created.

Flag Behavior

O_CREAT O_EXCL Semaphore Exists? Result
No No Yes Opens existing semaphore
No No No Error: ENOENT
Yes No Yes Opens existing (ignores value/mode)
Yes No No Creates new semaphore with value
Yes Yes Yes Error: EEXIST
Yes Yes No Creates new semaphore with value

Example: Creating a Named Semaphore

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>

int main(void)
{
    sem_t *sem;

    /* Create semaphore /myapp_lock with initial value 1
       Fail if it already exists (O_EXCL) */
    sem = sem_open("/myapp_lock", O_CREAT | O_EXCL, 0600, 1);
    if (sem == SEM_FAILED) {
        perror("sem_open");
        exit(EXIT_FAILURE);
    }

    printf("Semaphore created successfully.\n");

    /* ... use the semaphore ... */

    sem_close(sem);         /* close this process's handle */
    sem_unlink("/myapp_lock"); /* remove the semaphore name */
    return 0;
}

sem_close() – Close a Named Semaphore

#include <semaphore.h>

int sem_close(sem_t *sem);
/* Returns: 0 on success, -1 on error */

sem_close() closes the calling process’s association with the named semaphore. It is analogous to close() for file descriptors. Important points:

  • The semaphore is not removed from the system. It still exists for other processes.
  • Any locks held by this process on the semaphore are released.
  • If the process exits without calling sem_close(), the kernel automatically closes all semaphores opened by that process.
  • You must still call sem_unlink() separately to remove the semaphore from the filesystem.

sem_unlink() – Remove a Named Semaphore

#include <semaphore.h>

int sem_unlink(const char *name);
/* Returns: 0 on success, -1 on error */

sem_unlink() removes the semaphore name from the filesystem (i.e., deletes /dev/shm/sem.name). The semaphore itself is not destroyed until all processes that have it open call sem_close() — this is the same deferred destruction used by unlink() for files.

Named Semaphore Lifecycle

sem_open()
Create / Open
sem_wait()
Use (acquire)
sem_post()
Use (release)
sem_close()
Close handle
sem_unlink()
Remove name

Complete Cross-Process Example

Two programs share a named semaphore. The server creates it; the client opens it. The semaphore acts as a binary mutex protecting a shared counter in shared memory.

server.c – Creates the semaphore

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <unistd.h>

#define SEM_NAME "/demo_sem"

int main(void)
{
    sem_t *sem;

    /* Remove stale semaphore if present */
    sem_unlink(SEM_NAME);

    /* Create with initial value 1 (binary semaphore) */
    sem = sem_open(SEM_NAME, O_CREAT | O_EXCL, 0666, 1);
    if (sem == SEM_FAILED) {
        perror("sem_open");
        exit(EXIT_FAILURE);
    }

    printf("Server: semaphore created. Waiting 10 seconds...\n");
    sleep(10);

    printf("Server: cleaning up.\n");
    sem_close(sem);
    sem_unlink(SEM_NAME);
    return 0;
}

client.c – Opens the existing semaphore

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

#define SEM_NAME "/demo_sem"

int main(void)
{
    sem_t *sem;

    /* Open existing semaphore (no O_CREAT) */
    sem = sem_open(SEM_NAME, 0);
    if (sem == SEM_FAILED) {
        perror("sem_open");
        exit(EXIT_FAILURE);
    }

    printf("Client: acquired semaphore, locking...\n");
    sem_wait(sem);    /* lock */
    printf("Client: inside critical section.\n");
    sleep(2);
    sem_post(sem);    /* unlock */
    printf("Client: released semaphore.\n");

    sem_close(sem);
    return 0;
}

Run in two terminals:

# Terminal 1
gcc server.c -o server -lpthread && ./server

# Terminal 2 (while server is running)
gcc client.c -o client -lpthread && ./client

Common Errors

errno Cause Fix
ENOENT Opening a semaphore that does not exist (no O_CREAT) Add O_CREAT or ensure server created it first
EEXIST O_CREAT | O_EXCL but semaphore already exists Call sem_unlink first, or use O_CREAT without O_EXCL
EINVAL Name has bad format, or value > SEM_VALUE_MAX Name must start with / and have no other /
EACCES Permission denied on open Check mode bits passed at creation

Interview Questions – Named Semaphores

Q1. What is the correct format for a POSIX semaphore name?
The name must start with a single forward slash (/) and must not contain any other slashes. For maximum portability, keep the remainder to alphanumeric characters and underscores. Example: “/my_sem”. The leading slash is mandatory per SUSv3.
Q2. What is the difference between sem_close() and sem_unlink()?
sem_close() closes the calling process’s file descriptor to the semaphore — like close() for files. The semaphore still exists for other processes. sem_unlink() removes the semaphore’s name from the filesystem, and once all processes close it, the semaphore object is destroyed. Both calls are needed for full cleanup.
Q3. What happens if you call sem_open() with O_CREAT but without O_EXCL and the semaphore already exists?
sem_open() simply opens the existing semaphore. The mode and initial value arguments are silently ignored. The semaphore’s current value is whatever it was when last set.
Q4. If a process crashes without calling sem_close() or sem_unlink(), what happens?
The kernel automatically closes the process’s open semaphore handles on process exit (equivalent to sem_close). However, the named semaphore’s filesystem entry is NOT removed. It persists in /dev/shm/ until sem_unlink() is explicitly called. This is a common source of stale semaphore bugs — always call sem_unlink at the end, or call sem_unlink at the start of the server before sem_open to clean up leftovers.
Q5. How would you implement a simple mutual exclusion lock using a named semaphore?
Create the semaphore with initial value 1. Before entering the critical section, call sem_wait() to decrement to 0 (locking). After leaving the critical section, call sem_post() to increment back to 1 (unlocking). Any other process calling sem_wait() will block until the current holder posts.
Q6. What error would sem_open() return if a stale semaphore exists from a previous crash and you use O_CREAT | O_EXCL?
It returns SEM_FAILED with errno set to EEXIST. The standard pattern to handle this is to call sem_unlink(name) before sem_open() with O_CREAT | O_EXCL, or to use O_CREAT without O_EXCL if reopening an existing semaphore is acceptable.

Leave a Reply

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