Part 1: Creating & Opening Semaphore Sets with semget()

 

System V Semaphores
Chapter 47 — Part 1: Creating & Opening Semaphore Sets with semget()
47.1
TLPI Chapter Section
IPC
Inter-Process Communication
semget
Key System Call

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 — Overview

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 */

semget() — Parameters at a Glance
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 Argument — How Keys Work

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:

IPC_PRIVATE
Always creates a NEW unique set — not shared via key lookup
OR
ftok(path, proj)
Derives a key from a filename + project ID — two processes using the same path/proj get the same 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.

The nsems Argument — Number of Semaphores

System V semaphores are unique because they are organized in sets — a single semget() call can create multiple semaphores as one unit.

Semaphore Set: semid = 5, nsems = 3
2
sem[0]
0
sem[1]
5
sem[2]
Each semaphore is addressed by its index (0, 1, 2 …) when calling semctl() or semop()

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 — Flags and Permissions

The semflg argument is a bitwise combination of permission bits and control flags.

Permission Bits (same as file permissions)
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 vs IPC_CREAT | IPC_EXCL:
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.

Complete Working Example — Creating and Opening a Semaphore 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

IPC_PRIVATE — Parent-Child Semaphore Sharing

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;
}

Error Codes from semget()
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?

IPC_PRIVATE tells the kernel to generate a unique key — the set cannot be found by other processes via key lookup. It is used when related processes (parent/child) share the semid directly. ftok() derives a deterministic key from a file path — unrelated processes that call ftok() with the same arguments get the same key and can find the same semaphore set.
Q2. What happens if you call semget() with IPC_CREAT but without IPC_EXCL, and the set already exists?

semget() succeeds and returns the identifier of the existing set — it behaves like an open. No error is returned. To detect this case and fail intentionally, combine with IPC_EXCL.
Q3. Can you change the number of semaphores in an existing semaphore set?

No. Once a semaphore set is created with nsems semaphores, that number is fixed for the lifetime of the set. To change it you must remove the set with IPC_RMID and create a new one.
Q4. What is the initial value of semaphores in a newly created set?

On Linux, semaphore values are initialized to 0 when the set is created. However, this is implementation-specific and not guaranteed by POSIX. Always explicitly initialize semaphore values using semctl() with SETVAL or SETALL before using them.
Q5. What error does semget() return if you try to open an existing set but specify nsems larger than the actual set size?

EINVAL. This is a common trap — when opening (not creating) an existing set, nsems must be 0 or less than or equal to the actual number of semaphores in the set. Conventionally, pass 0 when opening.
Q6. Does the kernel automatically remove a System V semaphore set when all processes using it exit?

No. System V IPC objects (including semaphore sets) persist in the kernel until explicitly removed with semctl(IPC_RMID) or until the system reboots. This is a key difference from POSIX semaphores and a common source of resource leaks.
Q7. Why is it advisable to pass 0 as nsems when opening an existing semaphore set?

Because you might not know the exact size of the existing set — the opening process just wants the identifier. Passing any nonzero value greater than the actual set size would cause EINVAL. Passing 0 avoids this check entirely, making the open unconditional.

Next: semctl() — Controlling Semaphore Sets

Learn how to initialize, read, and remove semaphore sets using the powerful semctl() system call.

Part 2: semctl() → EmbeddedPathashala

Leave a Reply

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