System V Shared Memory

 

System V Shared Memory
Chapter 48 – TLPI | Linux IPC Series
Part 1
Introduction & Overview
IPC
Inter-Process Communication
Fast
Zero-Copy Data Sharing

What is Shared Memory?

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.

Key Terms You Must Know

Shared Memory Segment shmget() shmat() shmdt() shmctl() IPC_PRIVATE IPC_CREAT Attach / Detach Virtual Address Space Page Size Synchronization System V Semaphore

How Shared Memory Compares to Other IPC

The diagram below shows why shared memory is faster than pipes and message queues.

Pipes / Message Queues (two copies, two syscalls)
Process A
User Space
write buffer
copy ①
Kernel
Buffer
copy ②
Process B
User Space
read buffer

Shared Memory (zero copies, no per-access syscalls)
Process A
Virtual Address Space
ptr → shm
same physical RAM
Physical
Shared Page
One RAM page
Process B
Virtual Address Space
ptr → shm

The 5-Step Lifecycle of a Shared Memory Segment

Every shared memory usage follows this sequence. Each step maps to a specific system call.

1
Create / Openshmget(): create a new segment or get the ID of an existing one.
2
Attachshmat(): map the segment into the process’s virtual address space.
3
Use — read/write via the pointer returned by shmat(), just like normal memory.
4
Detachshmdt(): unmap the segment from this process’s address space. (Also happens automatically at process exit.)
5
Deleteshmctl(IPC_RMID): mark for deletion. Segment is destroyed once all processes detach.

Shared Memory vs Other IPC Mechanisms
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

Minimal Code Example: Shared Memory Skeleton

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

Why You Always Need Synchronization with Shared Memory

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

Terminology: “Attach” vs “Map”

You will encounter both terms when reading Linux IPC documentation.

System V API
Uses the word attach — you attach a segment to a process.
Function: shmat() / shmdt()
mmap() API
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.

Interview Questions — Introduction & Overview
Q1. Why is System V shared memory considered the fastest IPC mechanism?

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.

Q2. What are the five steps to use a shared memory segment?

(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.

Q3. If shared memory is so fast, why don’t we always use it?

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.

Q4. What happens to a shared memory segment when a process terminates without calling shmdt()?

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.

Q5. What is the difference between System V shared memory and POSIX shared memory (mmap)?

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.

Q6. What synchronization methods can be used with System V shared memory?

(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().

Series Navigation

System V Shared Memory – Chapter 48 | EmbeddedPathashala

Leave a Reply

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