shmget() — Creating & Opening Shared Memory

 

shmget() — Creating & Opening Shared Memory
Chapter 48, Section 48.2 – TLPI | System V Shared Memory
Part 2
shmget() Deep Dive
IPC_CREAT
Create / Open
key_t
Segment Key

What Does shmget() Do?

shmget() is the first call in any shared memory workflow. It either creates a brand-new shared memory segment and returns its integer ID, or opens an existing segment that was created by another process. Think of it as the open() of the shared memory world — the ID it returns is used in all subsequent calls.

The contents of a newly created segment are always initialized to zero by the kernel — you don’t need to memset() it.

Key Terms for This Section

shmget() key_t IPC_PRIVATE IPC_CREAT IPC_EXCL ftok() SHM_HUGETLB SHM_NORESERVE Page Size Segment Identifier shmflg Permissions

Function Signature
#include <sys/types.h>   /* For portability */
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);

/* Returns: shared memory segment identifier on success, or -1 on error */
Parameter Type Purpose
key key_t Unique identifier for the segment. Use IPC_PRIVATE for private segments or ftok() for shared.
size size_t Desired size in bytes (kernel rounds up to page size). When opening existing: must be ≤ actual segment size.
shmflg int Permission bits (e.g., 0600) ORed with control flags (IPC_CREAT, IPC_EXCL, etc.)

Understanding the key Argument

The key identifies the segment — it’s how unrelated processes agree to use the same segment. There are two ways to set it:

IPC_PRIVATE

Always creates a new, unique segment. No other process can discover this segment via its key — it’s meant to be shared only with child processes after fork(), or processes that are explicitly told the shmid.

ftok() — Generated Key

ftok(pathname, proj_id) generates a key from an existing file path and a project ID byte. Any two processes that call ftok() with the same path and project ID get the same key, so they can agree on a shared segment without out-of-band communication.

/* Method 1: IPC_PRIVATE — for parent-child sharing */
shmid = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0600);

/* Method 2: ftok() — for unrelated processes */
key_t key = ftok("/tmp/myapp.lock", 'A');
if (key == -1) { perror("ftok"); exit(1); }

/* Creator: */
shmid = shmget(key, 4096, IPC_CREAT | IPC_EXCL | 0600);

/* Other process (already knows the path): */
shmid = shmget(key, 4096, 0);   /* no IPC_CREAT — just open */

Size and Page Rounding

The kernel allocates shared memory in multiples of the system page size (typically 4096 bytes on x86). Your requested size is silently rounded up to the next page boundary. This matters for sizing your structs.

Requested
3000 bytes
kernel rounds up
page size = 4096
Allocated
4096 bytes
Requested
5000 bytes
kernel rounds up
Allocated
8192 bytes
#include <unistd.h>   /* sysconf() */

/* How to query the system page size at runtime */
long page_size = sysconf(_SC_PAGESIZE);
printf("Page size: %ld bytes\n", page_size);   /* typically 4096 */

/* Round your desired size up to a page boundary */
size_t desired   = 3000;
size_t allocated = ((desired + page_size - 1) / page_size) * page_size;
printf("Will allocate: %zu bytes\n", allocated);  /* 4096 */

shmflg Flags Reference
Flag Effect Notes
IPC_CREAT Create the segment if it doesn’t exist. Without this, opening a non-existent key returns ENOENT.
IPC_EXCL Fail with EEXIST if segment already exists. Used with IPC_CREAT for exclusive creation (like O_CREAT|O_EXCL).
0600 Owner read+write, no group/other access. Same octal permission bits as files. Checked by other processes opening the segment.
SHM_HUGETLB Use huge pages (requires CAP_IPC_LOCK). Linux 2.6+. Reduces TLB pressure for large segments. Non-standard.
SHM_NORESERVE Don’t reserve swap space for this segment. Linux 2.6.15+. Like MAP_NORESERVE for mmap(). Risk of SIGSEGV on write if OOM.

Common shmget() Patterns
Pattern 1: Always Create New (Exclusive)

Use when you are the sole creator; fail if something is already there.

/* IPC_CREAT | IPC_EXCL = fail if already exists */
shmid = shmget(key, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0600);
if (shmid == -1) {
    if (errno == EEXIST)
        fprintf(stderr, "Segment already exists!\n");
    else
        perror("shmget");
    exit(EXIT_FAILURE);
}
Pattern 2: Create or Open (Upsert)

Create if not existing, open if already there — useful for daemons.

/* IPC_CREAT without IPC_EXCL = create or open */
shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0600);
if (shmid == -1) { perror("shmget"); exit(EXIT_FAILURE); }
Pattern 3: Open Existing Only

The client pattern — assume the server already created the segment.

/* No IPC_CREAT — fail if segment does not exist */
shmid = shmget(key, 0, 0);    /* size=0 is valid when just opening */
if (shmid == -1) {
    if (errno == ENOENT)
        fprintf(stderr, "Server not started yet\n");
    else
        perror("shmget");
    exit(EXIT_FAILURE);
}

Full Example: Server Creates, Client Opens

A realistic pattern showing how two unrelated processes share a segment using ftok(). Both programs must be run from the same directory so ftok() produces identical keys.

/* shm_server.c — creates the segment and writes data */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

#define SHM_SIZE   1024
#define KEY_PATH   "/tmp"
#define KEY_ID     'S'

int main(void)
{
    key_t  key;
    int    shmid;
    char  *shm;

    /* Generate a key from a path + project byte */
    key = ftok(KEY_PATH, KEY_ID);
    if (key == -1) { perror("ftok"); exit(1); }

    /* Create the segment (exclusive — fail if already exists) */
    shmid = shmget(key, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0600);
    if (shmid == -1) { perror("shmget"); exit(1); }
    printf("[server] shmid=%d, key=0x%x\n", shmid, key);

    /* Attach */
    shm = shmat(shmid, NULL, 0);
    if (shm == (void *)-1) { perror("shmat"); exit(1); }

    /* Write data */
    snprintf(shm, SHM_SIZE, "Hello from server! PID=%d", getpid());
    printf("[server] wrote: %s\n", shm);
    printf("[server] press Enter to delete segment...\n");
    getchar();

    /* Detach and delete */
    shmdt(shm);
    shmctl(shmid, IPC_RMID, NULL);
    printf("[server] segment deleted\n");
    return 0;
}
/* shm_client.c — opens existing segment and reads data */
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define SHM_SIZE   1024
#define KEY_PATH   "/tmp"
#define KEY_ID     'S'

int main(void)
{
    key_t  key;
    int    shmid;
    char  *shm;

    /* Same ftok() args as server = same key */
    key = ftok(KEY_PATH, KEY_ID);
    if (key == -1) { perror("ftok"); exit(1); }

    /* Open existing segment (no IPC_CREAT) */
    shmid = shmget(key, 0, 0);
    if (shmid == -1) { perror("shmget"); exit(1); }

    /* Attach read-only (SHM_RDONLY) */
    shm = shmat(shmid, NULL, SHM_RDONLY);
    if (shm == (void *)-1) { perror("shmat"); exit(1); }

    printf("[client] read: %s\n", shm);

    shmdt(shm);
    return 0;
}

Compile and test:

gcc -o shm_server shm_server.c
gcc -o shm_client shm_client.c

# Terminal 1:
./shm_server

# Terminal 2 (while server is waiting):
./shm_client

SHM_HUGETLB — Huge Pages Explained

Modern x86 CPUs have a Translation Lookaside Buffer (TLB) — a small hardware cache that maps virtual page numbers to physical frame numbers. If your application allocates gigabytes of memory, the TLB fills up and causes frequent TLB misses, which are expensive.

Huge pages (2 MB or 1 GB instead of the usual 4 KB) let one TLB entry cover far more memory. The SHM_HUGETLB flag requests that the segment be backed by huge pages. Requires CAP_IPC_LOCK privilege and huge page pool pre-allocation.

Page size 1 GB memory needs TLB entries used TLB pressure
4 KB (normal) 262,144 entries 262,144 Very High
2 MB (huge) 512 entries 512 Low
/* SHM_HUGETLB example (requires root or CAP_IPC_LOCK) */
#include <sys/shm.h>

#define HUGE_SIZE  (2 * 1024 * 1024)  /* 2 MB — one huge page */

int shmid = shmget(IPC_PRIVATE, HUGE_SIZE,
                   IPC_CREAT | SHM_HUGETLB | 0600);
if (shmid == -1) {
    perror("shmget with SHM_HUGETLB");
    /* May fail if no huge pages configured: check /proc/sys/vm/nr_hugepages */
}
# Check/set huge page pool (as root):
cat /proc/sys/vm/nr_hugepages
echo 64 > /proc/sys/vm/nr_hugepages   # allocate 64 huge pages = 128 MB

Inspecting Segments with ipcs / ipcrm

System V IPC objects persist until explicitly deleted. Use the ipcs command to list them and ipcrm to delete stale segments.

# List all shared memory segments:
ipcs -m

# Sample output:
# ------ Shared Memory Segments --------
# key        shmid      owner      perms      bytes      nattch     status
# 0x00000000 131072     ravi       600        4096       0

# Delete a segment by ID:
ipcrm -m 131072

# Delete by key:
ipcrm -M 0x53455250

# Delete all shared memory (dangerous!):
ipcs -m | awk 'NR>3 { print $2 }' | xargs ipcrm -m 2>/dev/null

Important: If your program crashes before calling shmctl(IPC_RMID), the segment remains in the system. Always check with ipcs -m during development to avoid resource leaks.

/* Always clean up — even on error paths */
#include <signal.h>
#include <sys/shm.h>

static int g_shmid = -1;

void cleanup_handler(int sig)
{
    if (g_shmid != -1)
        shmctl(g_shmid, IPC_RMID, NULL);
    _exit(0);
}

int main(void)
{
    signal(SIGINT,  cleanup_handler);
    signal(SIGTERM, cleanup_handler);

    g_shmid = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0600);
    /* ... rest of program ... */
    return 0;
}

Common shmget() Errors
errno Cause Fix
EEXIST IPC_CREAT|IPC_EXCL and segment already exists. Remove stale segment with ipcrm, or drop IPC_EXCL.
ENOENT No IPC_CREAT, but key does not exist. Start the server first, or add IPC_CREAT.
EINVAL size > SHMMAX, or size < SHMMIN, or size > existing segment. Check /proc/sys/kernel/shmmax; pass size=0 when just opening.
ENOMEM Not enough memory to create segment. Reduce segment size; free up RAM.
ENOSPC System limit on number of segments (SHMMNI) reached. Clean up stale segments (ipcs -m / ipcrm).
EACCES Permission denied accessing an existing segment. Check segment permissions with ipcs -m.

System Limits for Shared Memory

The kernel enforces several limits. You can read and adjust them via /proc.

# Key limits (read current values):
cat /proc/sys/kernel/shmmax    # max size of single segment (bytes)
cat /proc/sys/kernel/shmall    # total pages of shared memory system-wide
cat /proc/sys/kernel/shmmni    # max number of segments system-wide
cat /proc/sys/kernel/shmmin    # min size (usually 1)

# Typical values on a modern Linux:
# shmmax: 18446744073692774399  (~unlimited on 64-bit)
# shmmni: 4096
# shmall: 18446744073692774399
/* Query limits programmatically with shmctl(IPC_INFO) */
#include <sys/shm.h>

struct shminfo info;
if (shmctl(0, IPC_INFO, (struct shmid_ds *) &info) == -1) {
    perror("shmctl IPC_INFO");
} else {
    printf("shmmax = %lu\n", info.shmmax);
    printf("shmmin = %lu\n", info.shmmin);
    printf("shmmni = %lu\n", info.shmmni);
    printf("shmall = %lu\n", info.shmall);
}

Interview Questions — shmget()
Q1. What is the purpose of shmget() and what does it return?

shmget() creates a new shared memory segment or opens an existing one identified by a key. It returns a non-negative integer called the shared memory identifier (shmid), which is used in subsequent shmat(), shmdt(), and shmctl() calls. Returns -1 on error.

Q2. What is IPC_PRIVATE and when would you use it?

IPC_PRIVATE (value 0) always creates a brand-new private segment. No other process can find it by key. It is used when the segment should be shared only between a parent and its children (after fork()), or between processes that communicate the shmid via some other channel (like a socket).

Q3. What is ftok() and how does it help two unrelated processes share memory?

ftok(pathname, proj_id) converts a file path + 1-byte project ID into a key_t. Two processes that call ftok() with the same arguments get the same key, so they can call shmget() with that key and both refer to the same segment — without needing to pass the shmid out-of-band.

Q4. If I request 3000 bytes, how much memory does the kernel actually allocate?

The kernel rounds up to the nearest page boundary. On a system with 4096-byte pages, 3000 bytes becomes 4096 bytes allocated. On the same system, 5000 bytes becomes 8192 bytes. The extra bytes are accessible but their content is undefined.

Q5. What is the difference between IPC_CREAT and IPC_CREAT | IPC_EXCL?

IPC_CREAT alone: create if not existing, open if already there. IPC_CREAT | IPC_EXCL: create only if not existing; fail with EEXIST if it already exists. This is analogous to open(O_CREAT) vs open(O_CREAT|O_EXCL).

Q6. What is SHM_HUGETLB and what problem does it solve?

SHM_HUGETLB creates the segment backed by huge pages (2 MB or 1 GB) instead of the normal 4 KB pages. It reduces TLB (Translation Lookaside Buffer) pressure — a 1 GB segment needs 512 TLB entries with 2 MB pages vs 262,144 entries with 4 KB pages. Useful for database buffers, HPC, and real-time applications. Requires CAP_IPC_LOCK privilege.

Q7. What happens if a program crashes without calling shmctl(IPC_RMID)?

The shared memory segment persists in the system — System V IPC objects are not automatically removed when all processes using them exit. They remain until explicitly removed with shmctl(IPC_RMID), or until the system reboots. This can lead to resource leaks. Always use ipcs -m during development to audit leftover segments.

Series Navigation

System V Shared Memory – Chapter 48 | EmbeddedPathashala

Leave a Reply

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