Named Semaphores sem_open() • sem_close() • sem_unlink()

 

Named Semaphores
sem_open() • sem_close() • sem_unlink() — Chapter 53, File 2 of 5

What are Named Semaphores?

Named semaphores are identified by a name string (like /mysem). Any process that knows the name can open the semaphore using sem_open(), even if the processes are completely unrelated. On Linux, named semaphores are stored as small shared-memory objects in a dedicated tmpfs filesystem mounted at /dev/shm. The actual file will appear as /dev/shm/sem.mysem.

They have kernel persistence: they survive after the creating process exits. They only disappear when explicitly deleted with sem_unlink() or when the system reboots.

Lifecycle of a Named Semaphore

sem_open()
Create or Open
sem_wait() / sem_post()
Use Semaphore
sem_close()
Close (per process)
sem_unlink()
Delete from System

sem_open() — Create or Open a Named Semaphore

#include <fcntl.h>      /* Defines O_* constants */
#include <sys/stat.h>   /* Defines mode constants */
#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 */

Arguments Explained

Argument Type Description
name const char * Name of semaphore. Must start with / and contain no other slashes. E.g., /mysem
oflag int 0 = open existing; O_CREAT = create if not exists; O_CREAT|O_EXCL = must create (fail if exists)
mode mode_t Permission bits (same as file permissions). Only used when creating. Masked by process umask.
value unsigned int Initial value of the semaphore. Only used when creating. Must be ≥ 0.
💡 Note on Permissions: SUSv3 doesn’t specify O_RDONLY/O_WRONLY/O_RDWR for semaphores. Linux assumes O_RDWR (read+write). So always grant both read and write permission bits (e.g., 0660 not 0440) to every user class that needs access.

oflag Combinations

oflag Semaphore Exists Semaphore Does NOT Exist
0 (no flags) ✅ Open it ❌ Fail (ENOENT)
O_CREAT ✅ Open existing (mode/value ignored) ✅ Create new with mode/value
O_CREAT | O_EXCL ❌ Fail (EEXIST) ✅ Create new with mode/value

Example 1: Open an Existing Semaphore

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

int main(void)
{
    sem_t *sem;

    /* Open existing semaphore - no mode/value needed */
    sem = sem_open("/shared_counter", 0);
    if (sem == SEM_FAILED) {
        perror("sem_open");   /* ENOENT if it doesn't exist */
        exit(EXIT_FAILURE);
    }

    printf("Opened existing semaphore\n");

    sem_close(sem);
    return 0;
}

Example 2: Create a New Semaphore (Exclusive)

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

int main(void)
{
    sem_t *sem;

    /*
     * O_CREAT | O_EXCL: create only if it doesn't exist.
     * Permissions: 0660 (owner+group: read+write)
     * Initial value: 1 (binary semaphore - like a mutex)
     */
    sem = sem_open("/my_mutex", O_CREAT | O_EXCL, 0660, 1);
    if (sem == SEM_FAILED) {
        perror("sem_open");   /* EEXIST if already exists */
        exit(EXIT_FAILURE);
    }

    printf("Created semaphore. Check: ls /dev/shm/sem.*\n");

    sem_close(sem);
    sem_unlink("/my_mutex");   /* Remove from system */
    return 0;
}

Example 3: Create or Open (Common Pattern)

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

int main(void)
{
    sem_t *sem;

    /*
     * O_CREAT only (no O_EXCL):
     * - Creates with value=3 if it doesn't exist
     * - Opens the existing one if it does (value/mode ignored)
     */
    sem = sem_open("/resource_pool", O_CREAT, 0660, 3);
    if (sem == SEM_FAILED) {
        perror("sem_open");
        exit(EXIT_FAILURE);
    }

    printf("Semaphore ready with up to 3 resources\n");
    /* Use sem_wait/sem_post to acquire/release */

    sem_close(sem);
    return 0;
}

Where are Named Semaphores Stored on Linux?

Linux stores named semaphores in a dedicated tmpfs filesystem mounted at /dev/shm. When you create a semaphore named /demo, it appears as /dev/shm/sem.demo.

This filesystem has kernel persistence: semaphore objects persist even when no process has them open, but they are lost on system reboot (since tmpfs is RAM-based).

/* Create a semaphore */
$ ./psem_create -cx /demo 666 1

/* Verify it exists on the filesystem */
$ ls -l /dev/shm/sem.*
-rw-rw---- 1 ravi users 16 Jun 12 10:00 /dev/shm/sem.demo

/* Each semaphore object is typically 16-32 bytes */
$ stat /dev/shm/sem.demo
⚠ umask affects permissions! If your process umask is 007, then specifying mode 0666 results in actual permissions 0660 (umask removes the ‘other’ bits). Always check your umask with umask command before testing.

Named Semaphores and fork()

When a process calls fork(), the child inherits all open named semaphore references from the parent. Both parent and child can then use the same semaphore to synchronize their actions.

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

int main(void)
{
    sem_t *sem;
    pid_t pid;

    /* Create semaphore, initial value 0 */
    sem = sem_open("/fork_sync", O_CREAT | O_EXCL, 0660, 0);
    if (sem == SEM_FAILED) { perror("sem_open"); exit(1); }

    pid = fork();
    if (pid == -1) { perror("fork"); exit(1); }

    if (pid == 0) {
        /* CHILD: do some work then signal parent */
        printf("Child: doing work...\n");
        sleep(1);
        printf("Child: work done, signaling parent\n");
        sem_post(sem);   /* increment: signal parent */
        sem_close(sem);
        exit(0);
    } else {
        /* PARENT: wait for child to complete */
        printf("Parent: waiting for child...\n");
        sem_wait(sem);   /* decrement: wait for child signal */
        printf("Parent: child signaled, continuing\n");
        wait(NULL);
        sem_close(sem);
        sem_unlink("/fork_sync");
    }

    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() removes the calling process’s association with the semaphore. It does not delete the semaphore from the system. Other processes that have the semaphore open can continue using it.

✅ sem_close() does:
  • Releases process’s open reference
  • Decrements open file description count
  • Frees any per-process resources
❌ sem_close() does NOT:
  • Delete the semaphore
  • Remove it from /dev/shm
  • Affect other processes using it
💡 Auto-close on exit: Open named semaphores are automatically closed when a process terminates (similar to file descriptors). However, the semaphore is NOT deleted from the system — you still need sem_unlink() for that.

sem_unlink() — Delete 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 system and marks the semaphore for deletion. The actual deletion happens only when all processes that have the semaphore open call sem_close() (or exit). This is similar to how unlink() works with files.

Time Process A Process B Semaphore State
T1 sem_open(“/s”) sem_open(“/s”) Open, 2 references
T2 sem_unlink(“/s”) still using it Name removed, semaphore still alive (B has it open)
T3 sem_close() still using it 1 reference remaining
T4 sem_close() 0 references → semaphore deleted

Complete Example: Two Processes Sharing a Named Semaphore

This example shows a server process creating a semaphore and a client process opening the same semaphore to coordinate access.

server.c (creates and manages semaphore)

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

#define SEM_NAME  "/ep_server_sem"

int main(void)
{
    sem_t *sem;

    /* Remove old semaphore if it exists from a previous crash */
    sem_unlink(SEM_NAME);

    /* Create fresh: binary semaphore, initial value 1 (unlocked) */
    sem = sem_open(SEM_NAME, O_CREAT | O_EXCL, 0660, 1);
    if (sem == SEM_FAILED) {
        perror("sem_open (server)");
        exit(EXIT_FAILURE);
    }

    printf("Server: semaphore '%s' created.\n", SEM_NAME);
    printf("Server: sleeping 10 seconds (start client now)...\n");
    sleep(10);

    printf("Server: shutting down, unlinking semaphore\n");
    sem_close(sem);
    sem_unlink(SEM_NAME);
    return 0;
}

client.c (opens and uses the existing semaphore)

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

#define SEM_NAME  "/ep_server_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 (client)");
        exit(EXIT_FAILURE);
    }

    printf("Client: acquired semaphore reference\n");

    /* Acquire (lock) */
    printf("Client: waiting to acquire lock...\n");
    if (sem_wait(sem) == -1) {
        perror("sem_wait");
        exit(EXIT_FAILURE);
    }
    printf("Client: lock acquired! In critical section\n");
    sleep(2);   /* simulate work */
    printf("Client: releasing lock\n");

    /* Release (unlock) */
    if (sem_post(sem) == -1) {
        perror("sem_post");
        exit(EXIT_FAILURE);
    }

    sem_close(sem);
    printf("Client: done\n");
    return 0;
}
/* Compile and run */
gcc server.c -o server -pthread
gcc client.c -o client -pthread

/* Terminal 1 */
./server

/* Terminal 2 (while server is running) */
./client

Common Errors and Their Meanings

errno Function Meaning
EEXIST sem_open O_CREAT|O_EXCL used and semaphore already exists
ENOENT sem_open, sem_unlink No semaphore with the given name
EACCES sem_open Permission denied
EINVAL sem_open Name is just “/” or value exceeds SEM_VALUE_MAX
ENAMETOOLONG sem_open Name is too long (exceeds PATH_MAX or NAME_MAX)

🅾 Interview Questions & Answers

Q1. What naming rules must a POSIX semaphore name follow?

The name must start with a forward slash (/) and must not contain any other slashes. For maximum portability, use only alphanumeric characters after the slash. Examples: /mysem, /counter_01. Invalid: mysem (no leading slash), /dir/sem (embedded slash).

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

sem_close() removes the calling process’s reference to the semaphore (like close() for a file descriptor). The semaphore continues to exist in the system. sem_unlink() removes the semaphore’s name from the system and marks it for deletion when all processes close it. Both are needed for complete cleanup.

Q3. What happens if a process exits without calling sem_close()?

The kernel automatically closes all open semaphores for a process when it exits, similar to file descriptors. However, the semaphore is not deleted from the system. It remains at /dev/shm/sem.name until sem_unlink() is called explicitly.

Q4. What does O_CREAT | O_EXCL guarantee?

It guarantees atomic creation: the semaphore is only created if it doesn’t already exist. If another process races to create the same semaphore at the same time, only one will succeed — the other will get EEXIST. This is the correct way to ensure only one process acts as the “creator”.

Q5. Why should you grant read AND write permissions when creating a semaphore?

On Linux, semaphores are implicitly opened with O_RDWR. Both sem_wait() (decrement, which is a write) and sem_post() (increment, also a write) modify the semaphore value. If a user class only has read permission, they cannot use sem_post/sem_wait. Use at least 0660, not 0440.

Q6. How are named semaphores stored on Linux?

As small POSIX shared memory objects in a dedicated tmpfs filesystem mounted at /dev/shm. A semaphore named /demo appears as /dev/shm/sem.demo. They persist until explicitly unlinked or the system reboots (tmpfs is RAM-based).

Q7. Can a child process use a named semaphore opened by the parent before fork()?

Yes. A child created by fork() inherits all named semaphore references that were open in the parent at the time of the fork. The parent and child can then use these semaphores to synchronize their actions, which is a common pattern for parent-child coordination.

Leave a Reply

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