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. |
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
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.
- Releases process’s open reference
- Decrements open file description count
- Frees any per-process resources
- Delete the semaphore
- Remove it from /dev/shm
- Affect other processes using it
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.
