#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
/* Returns: semaphore set identifier (semid) on success, -1 on error */
The semget() call either creates a new semaphore set or obtains the ID of an existing one. Think of it like open() for files — you get a handle (the semid) to work with.
The key is like a name for the semaphore set. Two processes can open the same semaphore set by using the same key.
Always creates a new, private semaphore set with a unique ID. No other process can accidentally find this by key — you must share the semid out-of-band (e.g., via fork inheritance or a file).
Two unrelated processes agree on a pathname and project ID, and both call ftok() to get the same key. They can then both open the same semaphore set by key.
Specifies how many semaphores to put in the set when creating a new set.
When opening an existing set, set nsems to 0 (the system ignores it) or specify the correct count.
Combines permission bits (same as file mode bits) with optional creation flags:
| Flag | Meaning |
|---|---|
IPC_CREAT |
Create the set if it does not exist. If it already exists, just open it. |
IPC_CREAT | IPC_EXCL |
Create the set. If it already exists, fail with EEXIST. Use this to guarantee you are the creator (and thus the initializer). |
0600 |
Owner read+write. These are the minimum permissions needed. |
0660 |
Owner + group read+write. For processes running as the same group. |
0666 |
Everyone read+write. Use when processes run as different users. |
When two unrelated processes need to access the same semaphore set, they need a way to agree on the same key. ftok() (“file to key”) generates a reproducible key_t from a pathname + project ID:
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
/* Returns: key_t value on success, -1 on error */
Takes the inode number of the file and the lower 8 bits of proj_id, combines them into a unique key. Both processes pass the same pathname + proj_id → both get the same key.
- The pathname must exist
- Both processes must use the same pathname
proj_idmust be non-zero- Key is NOT guaranteed unique if inode is reused
/* Example: two processes agree on key using ftok() */
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#define SEM_KEY_PATH "/tmp/myapp_sem_key" /* must exist */
#define SEM_PROJ_ID 'A' /* non-zero char */
int get_or_create_semaphore(int nsems) {
key_t key;
int semid;
/* Generate reproducible key from pathname + project char */
key = ftok(SEM_KEY_PATH, SEM_PROJ_ID);
if (key == -1) {
perror("ftok");
return -1;
}
printf("Generated key: 0x%x\n", key);
/* Try to create. If it already exists, just open it. */
semid = semget(key, nsems, IPC_CREAT | 0660);
if (semid == -1) {
perror("semget");
return -1;
}
return semid;
}
IPC_PRIVATE creates a truly private semaphore set. The classic use case is parent-child synchronization: the parent creates the semaphore before fork(), and both processes inherit the semid.
#include <sys/sem.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
/* Simple wrapper: subtract 1 (acquire) */
void sem_wait_op(int semid) {
struct sembuf sop = {0, -1, 0}; /* sem_num=0, sem_op=-1, sem_flg=0 */
if (semop(semid, &sop, 1) == -1) { perror("semop wait"); exit(1); }
}
/* Simple wrapper: add 1 (release) */
void sem_signal_op(int semid) {
struct sembuf sop = {0, +1, 0};
if (semop(semid, &sop, 1) == -1) { perror("semop signal"); exit(1); }
}
int main(void) {
int semid;
union semun arg;
pid_t pid;
/* Create private semaphore set with 1 semaphore */
semid = semget(IPC_PRIVATE, 1, S_IRUSR | S_IWUSR);
if (semid == -1) { perror("semget"); exit(1); }
/* Initialize semaphore to 0 (child will wait for parent) */
arg.val = 0;
if (semctl(semid, 0, SETVAL, arg) == -1) { perror("semctl"); exit(1); }
pid = fork();
if (pid == -1) { perror("fork"); exit(1); }
if (pid == 0) {
/* CHILD: wait until parent signals */
printf("Child [%d]: waiting for parent to signal...\n", getpid());
sem_wait_op(semid); /* blocks here: 0 - 1 would go below 0 */
printf("Child [%d]: received signal, now proceeding!\n", getpid());
exit(0);
} else {
/* PARENT: do some work, then signal child */
printf("Parent [%d]: doing work...\n", getpid());
sleep(2); /* simulate work */
printf("Parent [%d]: signaling child now\n", getpid());
sem_signal_op(semid); /* add 1 — wakes up child */
wait(NULL); /* wait for child to finish */
/* Clean up: delete the semaphore set */
if (semctl(semid, 0, IPC_RMID) == -1) { perror("semctl IPC_RMID"); }
printf("Parent: semaphore deleted\n");
}
return 0;
}
Parent [1001]: doing work…
Child [1002]: waiting for parent to signal…
(2 second pause)
Parent [1001]: signaling child now
Child [1002]: received signal, now proceeding!
Parent: semaphore deleted
When multiple unrelated processes start simultaneously, use IPC_CREAT | IPC_EXCL to guarantee only one process creates and initializes the semaphore:
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
#define KEY_PATH "/tmp/sem_demo"
#define KEY_ID 1
#define NUM_SEMS 1
int open_or_create_sem(void) {
key_t key;
int semid;
union semun arg;
key = ftok(KEY_PATH, KEY_ID);
if (key == -1) { perror("ftok"); exit(1); }
/* Try to create exclusively — only one process will succeed */
semid = semget(key, NUM_SEMS, IPC_CREAT | IPC_EXCL | 0600);
if (semid != -1) {
/* WE are the creator — initialize the semaphore */
printf("[%d] Created semaphore id=%d, initializing...\n", getpid(), semid);
arg.val = 1; /* binary semaphore: starts unlocked */
if (semctl(semid, 0, SETVAL, arg) == -1) {
perror("semctl SETVAL");
/* On failure, remove the set we created */
semctl(semid, 0, IPC_RMID);
exit(1);
}
printf("[%d] Initialized to 1\n", getpid());
} else if (errno == EEXIST) {
/* Set already exists — just open it */
semid = semget(key, 0, 0600);
if (semid == -1) { perror("semget open"); exit(1); }
printf("[%d] Opened existing semaphore id=%d\n", getpid(), semid);
} else {
perror("semget");
exit(1);
}
return semid;
}
int main(void) {
int semid = open_or_create_sem();
printf("[%d] Got semid = %d\n", getpid(), semid);
return 0;
}
IPC_CREAT | IPC_EXCL, there is still a tiny window between semget (create) and semctl (initialize) where another process might open the uninitialized set. Chapter 47 covers robust solutions to this using semctl IPC_STAT and the sem_otime field to detect whether initialization has completed.If the semaphore set was created elsewhere and you just want to get its ID:
/* Open existing semaphore set — no IPC_CREAT flag */
key_t key = ftok("/tmp/shared_app", 42);
if (key == -1) { perror("ftok"); exit(1); }
/* nsems=0 means "don't care about count, just open"
flags=0 means "no special flags, just get existing" */
int semid = semget(key, 0, 0);
if (semid == -1) {
if (errno == ENOENT)
fprintf(stderr, "Semaphore set does not exist yet!\n");
else
perror("semget");
exit(1);
}
printf("Opened semaphore set, semid = %d\n", semid);
| errno value | Meaning |
|---|---|
ENOENT |
No set exists for this key (and IPC_CREAT not specified) |
EEXIST |
Set already exists but IPC_CREAT | IPC_EXCL was specified |
EACCES |
Set exists but calling process lacks permission |
EINVAL |
nsems is 0 when creating, or nsems > existing count, or nsems > SEMMSL limit |
ENOSPC |
System limit on number of semaphore sets (SEMMNI) reached |
System V IPC objects use the same permission model as files (owner, group, other; read, write), but access meanings are slightly different:
Allows semctl GETVAL, GETALL, IPC_STAT. Lets you query semaphore values.
Allows semop(), semctl SETVAL, SETALL, IPC_RMID. Lets you modify semaphore values.
/* Common permission combinations */
/* Owner only — single-user applications */
semid = semget(key, 1, IPC_CREAT | 0600);
/* Owner + group — processes in same group (most common for daemons) */
semid = semget(key, 1, IPC_CREAT | 0660);
/* Everyone — when processes may run as different users */
semid = semget(key, 1, IPC_CREAT | 0666);
/* Check permissions with ipcs command:
$ ipcs -s
------ Semaphore Arrays --------
key semid owner perms nsems
0x00000000 0 ravi 600 1
0x00010203 1 ravi 660 3
*/
System V semaphores persist until explicitly deleted or the system reboots. Stale semaphores can cause problems in testing. Use these shell commands:
# List all semaphore sets
$ ipcs -s
# Show detailed info about a specific semid
$ ipcs -s -i 98307
# Remove a semaphore set by semid
$ ipcrm -s 98307
# Remove all semaphore sets (careful!)
$ ipcs -s | awk 'NR>2 { print $2 }' | xargs -I{} ipcrm -s {}
semctl(IPC_RMID), the semaphore set stays in the kernel. Running your program again may find a stale set with wrong values. Always use ipcs -s and ipcrm -s to clean up during development.Creating a set with 3 semaphores to protect different resources:
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
/* Semaphore indices — give them meaningful names */
#define SEM_MUTEX 0 /* protects critical section (init=1) */
#define SEM_PRODUCER 1 /* counts items to consume (init=0) */
#define SEM_CONSUMER 2 /* counts free slots (init=BUFFER_SIZE) */
#define NUM_SEMS 3
#define BUFFER_SIZE 10
int create_sem_set(key_t key) {
int semid;
union semun arg;
unsigned short init_vals[NUM_SEMS];
/* Create the set */
semid = semget(key, NUM_SEMS, IPC_CREAT | IPC_EXCL | 0660);
if (semid == -1) {
if (errno == EEXIST) {
/* Already exists — just open */
return semget(key, 0, 0660);
}
perror("semget");
return -1;
}
/* Initialize all semaphores at once using SETALL */
init_vals[SEM_MUTEX] = 1; /* unlocked */
init_vals[SEM_PRODUCER] = 0; /* no items yet */
init_vals[SEM_CONSUMER] = BUFFER_SIZE; /* all slots free */
arg.array = init_vals;
if (semctl(semid, 0, SETALL, arg) == -1) {
perror("semctl SETALL");
semctl(semid, 0, IPC_RMID); /* cleanup on failure */
return -1;
}
printf("Created semaphore set: semid=%d\n", semid);
printf(" sem[MUTEX] = %d\n", init_vals[SEM_MUTEX]);
printf(" sem[PRODUCER] = %d\n", init_vals[SEM_PRODUCER]);
printf(" sem[CONSUMER] = %d\n", init_vals[SEM_CONSUMER]);
return semid;
}
int main(void) {
key_t key;
int semid;
/* Create key file if it doesn't exist */
system("touch /tmp/sem_multitest");
key = ftok("/tmp/sem_multitest", 99);
if (key == -1) { perror("ftok"); exit(1); }
semid = create_sem_set(key);
if (semid == -1) { exit(1); }
printf("\nSemaphore set ready. semid = %d\n", semid);
printf("Use 'ipcs -s' to verify, 'ipcrm -s %d' to delete\n", semid);
return 0;
}
IPC_PRIVATE (value 0) always creates a brand-new semaphore set and gives it a unique ID. No other process can look it up by key — the semid must be communicated out-of-band (e.g., by inheritance via fork, or written to a file). ftok() generates a deterministic key from a pathname and project ID, so any unrelated process with access to the same file can generate the same key and use semget to open the same set. Use IPC_PRIVATE for parent-child; use ftok() for unrelated processes.sem_open(), System V IPC objects are kernel-persistent — they survive process termination and stay until explicitly deleted with semctl(IPC_RMID) or the system reboots. This is a common cause of resource leaks and must be handled carefully, especially when programs crash.IPC_CREAT alone creates if absent, or opens if present. This is ambiguous — you cannot tell whether you are the creator (and should initialize) or an opener (set already initialized). IPC_CREAT | IPC_EXCL succeeds only for the creator (fails with EEXIST otherwise). The process that succeeds knows it is the creator and is responsible for initialization with semctl.nsems specifies how many semaphores to create in the set when creating a new one. It must be ≥ 1 for creation. When opening an existing set (no IPC_CREAT, or IPC_CREAT without IPC_EXCL for an existing set), nsems should be 0 (ignored by the kernel) or may be set to the known count. Specifying a value larger than the existing set’s count causes EINVAL.