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.
#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.) |
The key identifies the segment — it’s how unrelated processes agree to use the same segment. There are two ways to set it:
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(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 */
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.
#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 */
| 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. |
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);
}
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); }
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);
}
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
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
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;
}
| 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. |
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);
}
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.
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).
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.
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.
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).
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.
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.
System V Shared Memory – Chapter 48 | EmbeddedPathashala
