shmget() – Creating Shared Memory System V Shared Memory

 

shmget() – Creating Shared Memory
Chapter 48 Part 2 – System V Shared Memory | EmbeddedPathashala
Part 2
shmget() Deep Dive
key_t
Segment Identification
Flags
IPC_CREAT, IPC_EXCL

shmget() is the first step in using System V shared memory. It either creates a new segment or opens an existing one, identified by a numeric key. It returns an integer ID (shmid) which is used in all subsequent operations on that segment.

Function Signature

#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);
/*
 * key    – IPC key identifying the segment (use ftok() or IPC_PRIVATE)
 * size   – size in bytes of the segment (rounded up to page size by kernel)
 * shmflg – flags: permissions | IPC_CREAT | IPC_EXCL
 *
 * Returns: shmid (non-negative integer) on success, -1 on error
 */

Parameter Type Meaning
key key_t Numeric identifier. Two processes with the same key access the same segment.
size size_t Bytes to allocate. Kernel rounds up to the nearest page boundary (usually 4096).
shmflg int OR-combination of permission bits (e.g. 0660) and flag constants.

Understanding key_t and ftok()

A key_t is just a 32-bit integer. It works like a “name” for the IPC object. Any two unrelated processes that use the same key value will get the same shared memory segment.

You can use a hardcoded number (not recommended for production), or use ftok() to generate one from a file path and a project ID:

#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);
/*
 * Derives a key from the file's inode number + device number + proj_id.
 * Both processes must use the same pathname AND proj_id to get the same key.
 * The file must exist and both processes must have permission to stat it.
 *
 * Returns: key_t on success, (key_t)-1 on error
 */
/* Example: two processes generating the same key */
key_t key = ftok("/var/myapp/shm.token", 'S');
if (key == (key_t)-1) {
    perror("ftok");
    exit(1);
}
/* key is now a consistent value both processes can use */
int shmid = shmget(key, 4096, IPC_CREAT | 0660);
if (shmid == -1) {
    perror("shmget");
    exit(1);
}
printf("shmid = %d\n", shmid);

IPC_PRIVATE (value 0): Pass this as the key when you want the kernel to always create a brand-new segment with no shareable key. Useful for parent-child IPC via fork() since the child inherits the shmid.

/* IPC_PRIVATE — for parent-child via fork() */
int shmid = shmget(IPC_PRIVATE, 8192, IPC_CREAT | 0600);
if (shmid == -1) { perror("shmget"); exit(1); }

pid_t pid = fork();
if (pid == 0) {
    /* child: inherits shmid, can call shmat() with it */
    char *shm = shmat(shmid, NULL, 0);
    printf("Child sees: %s\n", shm);
    shmdt(shm);
    exit(0);
}
/* parent: write something */
char *shm = shmat(shmid, NULL, 0);
strcpy(shm, "Hello child!");
wait(NULL);
shmdt(shm);
shmctl(shmid, IPC_RMID, NULL);

shmflg – Flags Explained

Flag Meaning
IPC_CREAT Create segment if it doesn’t exist. If it exists, open it (no error).
IPC_EXCL Used with IPC_CREAT — fail with EEXIST if segment already exists. Guarantees creation.
0600 etc. Unix permission bits (octal). Controls who can attach or modify the segment.
SHM_HUGETLB Use huge pages (2MB or 1GB). Reduces TLB pressure for very large segments. Needs CAP_IPC_LOCK.
SHM_NORESERVE Do not reserve swap space for this segment. Risky — process may get SIGSEGV on write if swap is full.

Pattern 1: Create Only
IPC_CREAT | IPC_EXCL | 0660
Fails if segment exists. Use in server startup to ensure fresh state.
Pattern 2: Open or Create
IPC_CREAT | 0660
Opens existing or creates new. Simple but no guarantee of fresh state.
Pattern 3: Open Only
0 (no flags)
Fails with ENOENT if segment doesn’t exist. Use in client code.

Size and Page Rounding

The kernel rounds the requested size up to a multiple of the system page size (typically 4096 bytes). If you request 3000 bytes, you actually get 4096. If you request 5000 bytes, you get 8192. The extra bytes are accessible but you should not rely on them being zeroed or having any particular content.

Request: 3000 bytes
Allocated: 4096 bytes
+1096 bytes padding
Request: 5000 bytes
Allocated: 8192 bytes
+3192 bytes padding
Request: 4096 bytes
Allocated: 4096 bytes
Exact, no padding
#include <unistd.h>
#include <stdio.h>

/* Get system page size at runtime */
long page_size = sysconf(_SC_PAGESIZE);
printf("Page size: %ld bytes\n", page_size);

/* Round up size manually before calling shmget */
size_t wanted = 3000;
size_t actual = (wanted + page_size - 1) & ~(page_size - 1);
printf("Requested: %zu, Will get: %zu\n", wanted, actual);
/* Output: Requested: 3000, Will get: 4096 */

Complete Two-Process Example

Server creates the segment, writes data. Client opens it and reads.

/* === server.c === */
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define SHM_KEY  0x4321
#define SHM_SIZE 512

int main(void) {
    /* Create segment — fail if already exists */
    int shmid = shmget(SHM_KEY, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0660);
    if (shmid == -1) {
        perror("shmget");  /* EEXIST if already running */
        exit(1);
    }
    printf("Server: created shmid=%d\n", shmid);

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

    /* Write a message */
    snprintf(shm, SHM_SIZE, "Temperature: 36.5 C, Humidity: 72%%");
    printf("Server: wrote data, waiting 10s...\n");
    sleep(10);  /* Let client read */

    /* Cleanup */
    shmdt(shm);
    shmctl(shmid, IPC_RMID, NULL);
    printf("Server: deleted segment\n");
    return 0;
}
/* gcc -o server server.c */
/* === client.c === */
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>

#define SHM_KEY  0x4321
#define SHM_SIZE 512

int main(void) {
    /* Open existing segment — no IPC_CREAT */
    int shmid = shmget(SHM_KEY, SHM_SIZE, 0);
    if (shmid == -1) {
        perror("shmget");  /* ENOENT if server not started yet */
        exit(1);
    }
    printf("Client: opened shmid=%d\n", shmid);

    /* Attach read-only — we are just reading */
    char *shm = (char *)shmat(shmid, NULL, SHM_RDONLY);
    if (shm == (char *)-1) { perror("shmat"); exit(1); }

    printf("Client: read: %s\n", shm);

    shmdt(shm);
    return 0;
}
/* gcc -o client client.c
 * Run: ./server &  then  ./client
 */

Common Errors from shmget()

errno Cause Fix
EEXIST IPC_CREAT|IPC_EXCL used but segment already exists Delete old segment with ipcrm or shmctl(IPC_RMID)
ENOENT No IPC_CREAT but segment doesn’t exist Start server first
EINVAL Size smaller than SHMMIN or larger than SHMMAX, or size 0 Check /proc/sys/kernel/shmmax
ENOMEM Not enough RAM/swap Reduce size or free memory
ENOSPC Max number of segments (SHMMNI) reached Run ipcs -m to find and delete leaked segments
EACCES Permission denied on existing segment Check segment permissions with ipcs -m

Useful shell commands for debugging:

# List all shared memory segments
ipcs -m

# Delete a specific segment by shmid
ipcrm -m <shmid>

# Check system limits
cat /proc/sys/kernel/shmmax    # max segment size in bytes
cat /proc/sys/kernel/shmall    # max total pages of shared memory
cat /proc/sys/kernel/shmmni    # max number of segments

Interview Questions

Q1. What is the difference between IPC_CREAT alone vs IPC_CREAT|IPC_EXCL?

IPC_CREAT alone opens an existing segment if one with that key already exists, or creates it if it doesn’t — essentially an “open or create” operation. Adding IPC_EXCL makes it a strict create — it fails with EEXIST if the segment already exists. Use IPC_EXCL in server startup to guarantee you’re starting with a fresh, uninitialized segment, not accidentally reusing stale data.

Q2. What does shmget() actually return and how is it used?

It returns a non-negative integer called the shmid (shared memory identifier). This is the kernel’s internal ID for the segment. All subsequent operations — shmat(), shmdt(), shmctl() — take the shmid as their first argument. The shmid is local to the machine and changes across reboots or segment recreation.

Q3. How does ftok() work? What are its limitations?

ftok() generates a key_t by combining the file’s inode number, device number, and the supplied proj_id byte. Its limitation is that if the file is deleted and recreated, its inode number changes, producing a different key — even if the path looks the same. Also, inode number collisions can theoretically produce the same key for different files. For robust code, use a hardcoded unique key or a proper namespace mechanism.

Q4. What happens if you call shmget() with size=0?

If you’re opening an existing segment (no IPC_CREAT), passing size=0 is fine — the kernel ignores the size and returns the existing segment’s ID. If you’re creating a new segment, size=0 causes EINVAL because you must specify a positive size.

Q5. How do you clean up leaked shared memory segments?

System V shared memory persists until explicitly deleted. If a process crashes before calling shmctl(IPC_RMID), the segment leaks. You can clean up with: ipcs -m to list all segments, then ipcrm -m <shmid> to remove each one. In production code, install a signal handler for SIGTERM/SIGINT that calls shmctl(IPC_RMID) before exiting.

Chapter 48 – System V Shared Memory Series

EmbeddedPathashala.com | Free Embedded & Linux Tutorials

Leave a Reply

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