What is a Semaphore?
A semaphore is a kernel-maintained integer variable used for synchronizing access to shared resources between processes. Unlike mutexes which are binary (locked/unlocked), a semaphore can hold any non-negative integer value, making it more flexible for controlling concurrent access.
System V semaphores are one of the three classic System V IPC mechanisms alongside message queues and shared memory. They are organized as sets — a single kernel object can contain multiple semaphore values grouped together.
Think of a semaphore as a counter that represents “how many resources are currently available.” A value of 0 means no resources are free — any process trying to decrement it will block until another process increments it.
The semget() system call does two things depending on how you call it:
- Create a new semaphore set in the kernel
- Obtain the identifier of an already-existing semaphore set
#include <sys/types.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
/* Returns: semaphore set identifier (semid) on success, -1 on error */
| Parameter | Type | Purpose |
| key | key_t | IPC key — identifies which semaphore set to create/open. Use IPC_PRIVATE or ftok(). |
| nsems | int | Number of semaphores in the set. Must be > 0 when creating. Must be ≤ existing set size when opening. |
| semflg | int | Permission bits OR’d with IPC_CREAT / IPC_EXCL flags. E.g., IPC_CREAT | 0660 |
The key identifies the semaphore set in the kernel. Two processes can access the same semaphore set if they use the same key. There are two common ways to supply a key:
/* Method 1: IPC_PRIVATE — used when parent shares semid with child via fork() */
int semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
if (semid == -1) {
perror("semget IPC_PRIVATE");
exit(EXIT_FAILURE);
}
/* semid is passed to child process through inheritance after fork() */
/* Method 2: ftok() — used when unrelated processes need to share */
key_t key = ftok("/tmp/myapp", 'S');
if (key == -1) {
perror("ftok");
exit(EXIT_FAILURE);
}
int semid2 = semget(key, 1, IPC_CREAT | 0660);
if (semid2 == -1) {
perror("semget ftok");
exit(EXIT_FAILURE);
}
Important: IPC_PRIVATE does NOT mean the semaphore is truly private — any process that knows the semid can use it. The name is misleading; it simply means the key is not used for lookup.
System V semaphores are unique because they are organized in sets — a single semget() call can create multiple semaphores as one unit.
Rules for nsems:
- When creating a new set: nsems must be > 0
- When opening an existing set: nsems must be ≤ existing set size (or EINVAL error)
- You cannot change the number of semaphores in an existing set
- Semaphores are numbered starting from 0
/* Creating a set of 3 semaphores */
int semid = semget(key, 3, IPC_CREAT | 0660);
/* Opening an existing set — nsems can be 0 to actual_nsems */
/* If actual set has 3 semaphores, these are valid: */
int semid_open = semget(key, 1, 0660); /* OK: 1 <= 3 */
int semid_bad = semget(key, 5, 0660); /* FAILS: 5 > 3 — returns EINVAL */
/* Conventionally, pass 0 when just opening an existing set */
int semid_conv = semget(key, 0, 0660); /* Most portable approach */
The semflg argument is a bitwise combination of permission bits and control flags.
| Octal | Meaning |
| 0400 | Owner read (GETVAL, GETALL, etc.) |
| 0200 | Owner write/alter (SETVAL, SETALL, semop) |
| 0040 | Group read |
| 0020 | Group write/alter |
| 0004 | Other read |
| 0002 | Other write/alter |
/* IPC_CREAT: create the set if it doesn't exist */
/* IPC_EXCL: fail with EEXIST if set already exists */
/* Create new set exclusively — fails if already exists */
int semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0600);
if (semid == -1) {
if (errno == EEXIST) {
printf("Semaphore set already exists!\n");
} else {
perror("semget");
}
exit(EXIT_FAILURE);
}
/* Create or open existing (no IPC_EXCL) */
int semid2 = semget(key, 1, IPC_CREAT | 0660);
/* Open existing only — no IPC_CREAT */
int semid3 = semget(key, 0, 0660);
if (semid3 == -1) {
/* Set does not exist */
perror("semget: set not found");
}
IPC_CREAT alone is idempotent — if the set exists, it opens it. Adding IPC_EXCL makes it fail if the set already exists, like O_CREAT | O_EXCL for files. Use IPC_EXCL when you must guarantee you’re the one creating a fresh set.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#define KEY_PATH "/tmp/sem_demo"
#define KEY_PROJ 'A'
#define NUM_SEMS 2
int main(void)
{
key_t key;
int semid;
/* Step 1: Generate IPC key from pathname + project id */
key = ftok(KEY_PATH, KEY_PROJ);
if (key == -1) {
perror("ftok");
exit(EXIT_FAILURE);
}
printf("Generated key: 0x%x\n", key);
/* Step 2: Create semaphore set with IPC_EXCL to detect duplicates */
semid = semget(key, NUM_SEMS, IPC_CREAT | IPC_EXCL | 0660);
if (semid == -1) {
if (errno == EEXIST) {
/* Set already exists — open it instead */
printf("Set exists. Opening existing set...\n");
semid = semget(key, 0, 0660);
if (semid == -1) {
perror("semget open");
exit(EXIT_FAILURE);
}
} else {
perror("semget create");
exit(EXIT_FAILURE);
}
} else {
printf("New semaphore set created.\n");
}
printf("Semaphore set ID: %d\n", semid);
printf("Number of semaphores: %d\n", NUM_SEMS);
printf("Semaphores numbered 0 to %d\n", NUM_SEMS - 1);
/*
* At this point semid is valid.
* Semaphores are uninitialized (value = 0 on Linux).
* You must call semctl(SETVAL or SETALL) before using them.
*/
return 0;
}
Compile: gcc -o sem_demo sem_demo.c — Run: create /tmp/sem_demo file first with touch /tmp/sem_demo
When a parent process creates a semaphore with IPC_PRIVATE before forking, the child inherits the semid and both can synchronize using the same semaphore set.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/wait.h>
int main(void)
{
int semid;
pid_t pid;
/*
* IPC_PRIVATE: kernel picks a unique key.
* 1 semaphore in the set.
* Permissions 0600 (owner read+write only)
*/
semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
if (semid == -1) {
perror("semget");
exit(EXIT_FAILURE);
}
printf("Parent: created semaphore set, semid=%d\n", semid);
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) {
/* Child: semid is inherited — same semaphore set */
printf("Child: I have semid=%d too!\n", semid);
/*
* Both parent and child can now call semop() on semid
* to synchronize their execution
*/
exit(EXIT_SUCCESS);
}
wait(NULL);
printf("Parent: child finished\n");
/*
* Clean up — parent should remove the semaphore set
* after use (kernel does NOT auto-remove it)
*/
/* semctl(semid, 0, IPC_RMID) would go here */
return 0;
}
| errno | Cause |
| EACCES | No permission to access the existing set |
| EEXIST | IPC_CREAT | IPC_EXCL specified but set already exists |
| EINVAL | nsems <= 0 when creating, or nsems > existing set size when opening |
| ENOENT | No set with this key and IPC_CREAT not specified |
| ENOSPC | System-wide limit on semaphore sets or total semaphores exceeded |
Interview Questions — semget()
Next: semctl() — Controlling Semaphore Sets
Learn how to initialize, read, and remove semaphore sets using the powerful semctl() system call.
