System V shared memory is the fastest IPC mechanism available on Linux/Unix. It allows two or more processes to directly share a region of physical memory — called a segment. Once attached, reading and writing the shared memory is exactly like reading and writing a normal C array or struct — no system calls needed per access.
Contrast this with pipes or message queues: there, data has to be copied from userspace into the kernel, then copied again into the receiving process. With shared memory, no copying happens at all. One process writes — all others see it immediately, because they all refer to the same physical RAM.
The trade-off: since the kernel doesn’t mediate every access, you must add synchronization yourself (usually System V semaphores, POSIX semaphores, or mutexes) to avoid races.
The diagram below shows why shared memory is faster than pipes and message queues.
Every shared memory usage follows this sequence. Each step maps to a specific system call.
shmget(): create a new segment or get the ID of an existing one.shmat(): map the segment into the process’s virtual address space.shmat(), just like normal memory.shmdt(): unmap the segment from this process’s address space. (Also happens automatically at process exit.)shmctl(IPC_RMID): mark for deletion. Segment is destroyed once all processes detach.| Feature | Shared Memory | Pipes / FIFOs | Msg Queues |
|---|---|---|---|
| Speed | Fastest — no copy | 2 copies per msg | 2 copies per msg |
| Kernel mediation | No (per access) | Yes | Yes |
| Synchronization | Manual required | Built-in (atomic) | Built-in |
| Data persistence | Until IPC_RMID | Until read / closed | Until received |
| Data structure | Any (raw bytes) | Byte stream | Typed messages |
| Processes | 2 or more | 2 (one-directional) | 2 or more |
This skeleton shows the overall flow — create, attach, use, detach, delete. Details of each call are covered in the following parts.
/* shm_skeleton.c — Minimal System V shared memory lifecycle */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#define SHM_SIZE 4096 /* 4 KB — kernel rounds up to page size anyway */
int main(void)
{
int shmid;
char *shmptr;
/* STEP 1: Create a shared memory segment.
IPC_PRIVATE means only this process (and its children) will use it.
0600 = owner can read+write */
shmid = shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0600);
if (shmid == -1) {
perror("shmget");
exit(EXIT_FAILURE);
}
printf("Segment created, shmid = %d\n", shmid);
/* STEP 2: Attach — map segment into our address space.
NULL means: let the kernel pick the address (recommended). */
shmptr = shmat(shmid, NULL, 0);
if (shmptr == (void *) -1) {
perror("shmat");
exit(EXIT_FAILURE);
}
printf("Attached at address: %p\n", (void *)shmptr);
/* STEP 3: Use — read/write like normal memory */
strcpy(shmptr, "Hello from shared memory!");
printf("Written: %s\n", shmptr);
/* STEP 4: Detach */
if (shmdt(shmptr) == -1) {
perror("shmdt");
exit(EXIT_FAILURE);
}
printf("Detached\n");
/* STEP 5: Delete */
if (shmctl(shmid, IPC_RMID, NULL) == -1) {
perror("shmctl IPC_RMID");
exit(EXIT_FAILURE);
}
printf("Segment deleted\n");
return 0;
}
Compile and run:
gcc -o shm_skeleton shm_skeleton.c
./shm_skeleton
Because the kernel does not intervene on every read/write, two processes can simultaneously modify the same location — a race condition. The classic example:
| Time | Process A | Process B | Shared counter |
|---|---|---|---|
| T1 | reads counter → 5 | reads counter → 5 | 5 |
| T2 | adds 1 → 6, writes 6 | (not scheduled) | 6 |
| T3 | (not scheduled) | adds 1 to its stale 5 → 6, writes 6 | 6 ← WRONG! Should be 7 |
Solution: protect accesses with a semaphore, mutex, or other locking primitive. System V semaphores (Chapter 47) are the traditional companion to System V shared memory.
/* Pattern: use a semaphore to protect shared memory access */
sem_wait(&sem); /* lock */
shared_data->counter++; /* safe — only one process at a time */
sem_post(&sem); /* unlock */
You will encounter both terms when reading Linux IPC documentation.
Uses the word attach — you attach a segment to a process.
Function:
shmat() / shmdt()Uses the word map — you map a region at an address.
Function:
mmap() / munmap()The underlying operation is identical — both modify the process’s page table to point virtual addresses at physical RAM pages. The terminology difference is purely historical.
Because data is never copied. Both processes map the same physical pages into their virtual address spaces. A write by one process is immediately visible to all others with no kernel involvement per access. Pipes and message queues require two copies (user→kernel, kernel→user) and a system call each way.
(1) shmget() — create/open, get ID.
(2) shmat() — attach (map into virtual address space).
(3) Read/write through the returned pointer.
(4) shmdt() — detach.
(5) shmctl(IPC_RMID) — delete the segment.
Because you must add explicit synchronization. Without it, concurrent accesses cause race conditions. Pipes and message queues have synchronization built in. The added complexity makes shared memory more error-prone and harder to debug.
shmdt() is called automatically by the kernel on process exit. The segment itself persists until shmctl(IPC_RMID) is called by some process, or until the system reboots. Unlike pipes, System V IPC objects survive process death.
Both expose the same physical memory sharing. System V uses integer keys and shmget/shmat/shmdt/shmctl. POSIX uses shm_open() to create a named file-like object then mmap() to map it — closer to normal file I/O. POSIX is considered more portable and modern; System V is older but still widely used.
(a) System V semaphores (semget/semop) — the natural companion.
(b) POSIX semaphores (sem_open/sem_wait/sem_post).
(c) POSIX mutexes with PTHREAD_PROCESS_SHARED attribute, placed inside the shared memory segment itself.
(d) File locks (advisory) via flock() or fcntl().
System V Shared Memory – Chapter 48 | EmbeddedPathashala
