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()
Q1. What is the difference between IPC_PRIVATE and ftok() when creating a semaphore set?
Q2. What happens if you call semget() with IPC_CREAT but without IPC_EXCL, and the set already exists?
Q3. Can you change the number of semaphores in an existing semaphore set?
Q4. What is the initial value of semaphores in a newly created set?
Q5. What error does semget() return if you try to open an existing set but specify nsems larger than the actual set size?
Q6. Does the kernel automatically remove a System V semaphore set when all processes using it exit?
Q7. Why is it advisable to pass 0 as nsems when opening an existing semaphore set?
Next: semctl() — Controlling Semaphore Sets
Learn how to initialize, read, and remove semaphore sets using the powerful semctl() system call.
