Comparing Shared Memory Techniques System V vs POSIX Shared Memory vs Shared File Mappings

 

Comparing Shared Memory Techniques
System V vs POSIX Shared Memory vs Shared File Mappings
3
Techniques
54.5
TLPI Section
IPC
Performance

Why Compare Them?

Linux gives you three main mechanisms to share memory between unrelated processes: System V shared memory, shared file mappings, and POSIX shared memory objects. All three do the same fundamental thing — map the same physical pages into multiple address spaces. But they differ significantly in interface, naming, flexibility, and backing store.

There is also a fourth technique — anonymous mappings with MAP_SHARED — but this only works between related processes (parent and child after fork()), so it is a separate case.

What All Three Have in Common

Despite their differences, all three shared memory techniques share the same fundamental properties:

Fast IPC

All provide the fastest possible IPC — data lives in RAM and is accessed directly via pointer dereference. No kernel copy happens during data exchange.

Needs Synchronization

None provide built-in synchronization. Applications must use a semaphore (POSIX or System V) or mutex to coordinate access — otherwise concurrent writes corrupt data.

Looks Like Normal Memory

Once mapped, the shared region appears as a normal range in the process’s virtual address space. Access it with pointers like any other memory.

Use Offsets, Not Pointers

The region is mapped at different virtual addresses in each process. Any references stored inside the shared region must be offsets from the region’s start, not absolute pointers.

The /proc/PID/maps file lists all mapped regions for any process, including all three types of shared memory. You can inspect this with cat /proc/self/maps.

Side-by-Side Comparison

This table is based directly on Section 54.5 of TLPI:

Feature System V
Shared Memory
Shared File
Mapping
POSIX Shared
Memory ✓
Identification Integer key + identifier
(non-standard)
Filesystem path
(normal file)
String name like
/myobj
API style Own calls: shmget,
shmat, shmctl
Standard UNIX:
open, mmap
UNIX-like:
shm_open, mmap
Backed by disk? No (RAM only) Yes — real file No (tmpfs/RAM)
Data persists
after reboot?
No Yes No
Size at creation Fixed via shmget() Resizable with
ftruncate()
Resizable with
ftruncate()
File descriptor? No (own identifier) Yes (from open()) Yes (from shm_open())
Standard tools
(ls, stat, fchmod)
No — use ipcs Yes Yes (via /dev/shm/)
Deletion command shmctl(IPC_RMID)
or ipcrm
unlink() shm_unlink()
or rm /dev/shm/
Portability Widely available
(older systems)
Universal UNIX Most modern UNIX
(may lack on old)
Overhead vs
file mapping
Similar Disk I/O overhead Lower — no disk

Which Technique Should You Use?

The choice comes down to one key question: do you need data to survive a reboot?

Do you need data to survive a reboot?
↙ YES
Shared File Mapping
open() + mmap()
Data written to real disk file
↘ NO
POSIX Shared Memory ✓
shm_open() + mmap()
Pure RAM, no disk overhead
System V Shared Memory? Avoid in new code. Its non-standard API is harder to use and does not integrate with standard UNIX tools. Use POSIX shared memory instead unless you need compatibility with very old systems.

The Key Problem with System V’s Naming Scheme

System V IPC (shared memory, semaphores, message queues) uses keys and identifiers that don’t fit the UNIX file model. Here is why this causes problems:

System V: Non-standard
/* Need special commands */
ipcs -m          /* list shared memory */
ipcrm -m shmid   /* delete it */

/* Need separate system calls */
int id = shmget(key, size, IPC_CREAT | 0600);
void *p = shmat(id, NULL, 0);
shmctl(id, IPC_RMID, NULL);

/* Key generation is awkward */
key_t key = ftok("/some/file", 1);
POSIX: Integrates with UNIX
/* Standard shell tools work */
ls /dev/shm/
rm /dev/shm/myobj
stat /dev/shm/myobj

/* Familiar fd-based API */
int fd = shm_open("/myobj",
          O_CREAT | O_RDWR, 0600);
fstat(fd, &sb);   /* get size */
fchmod(fd, 0644); /* change perms */
mmap(..., fd, 0); /* map it */
shm_unlink("/myobj");

Size Flexibility: POSIX Wins

System V shared memory has a fixed size set at creation time via shmget(). You cannot grow or shrink it without deleting and recreating it (which requires all attached processes to detach first).

POSIX shared memory and shared file mappings support dynamic resizing via ftruncate(). You can grow the object and remap it. Here is the pattern:

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define SHM_NAME "/ep_resize"

int main(void)
{
    int   fd;
    void *addr;
    size_t old_size = 4096;
    size_t new_size = 8192;

    fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
    if (fd == -1) { perror("shm_open"); exit(1); }

    /* Initial size */
    ftruncate(fd, old_size);

    addr = mmap(NULL, old_size, PROT_READ | PROT_WRITE,
                MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED) { perror("mmap"); exit(1); }

    /* ... use addr for old_size bytes ... */

    /* Now grow the object */
    ftruncate(fd, new_size);

    /* Must remap — old mapping only covers old_size */
    munmap(addr, old_size);
    addr = mmap(NULL, new_size, PROT_READ | PROT_WRITE,
                MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED) { perror("mmap remap"); exit(1); }

    printf("Remapped at new size: %zu bytes\n", new_size);

    /* Cleanup */
    munmap(addr, new_size);
    close(fd);
    shm_unlink(SHM_NAME);
    return 0;
}

Linux extra: mremap(old_addr, old_size, new_size, MREMAP_MAYMOVE) can extend a mapping in-place (or move it) without a full unmap/remap cycle. This is Linux-specific.

Anonymous mmap vs POSIX Shared Memory

There is one more technique worth knowing: anonymous shared mappings (mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0)). This is used between processes related via fork() — parent and child share the same anonymous pages. It cannot be used between completely unrelated processes because there is no name to share.

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>

int main(void)
{
    int *shared;

    /* Create anonymous shared mapping BEFORE fork() */
    shared = mmap(NULL, sizeof(int),
                  PROT_READ | PROT_WRITE,
                  MAP_SHARED | MAP_ANONYMOUS,
                  -1,    /* no fd needed */
                  0);
    if (shared == MAP_FAILED) { perror("mmap"); exit(1); }

    *shared = 0;  /* initialize */

    if (fork() == 0) {
        /* Child: write to shared memory */
        *shared = 42;
        printf("Child wrote: %d\n", *shared);
        exit(0);
    }

    wait(NULL);
    printf("Parent read: %d\n", *shared);  /* sees 42 */

    munmap(shared, sizeof(int));
    return 0;
}

This cannot be done with unrelated processes. For those, use POSIX shared memory (with a name they both know) or System V shared memory (with a key they agree on).

Inspecting Shared Memory in /proc/PID/maps

All three shared memory types appear in /proc/PID/maps. Here is how to read it:

cat /proc/self/maps
# Example output (relevant lines):

# POSIX shared memory:
7f3a10000000-7f3a10001000 rw-s 00000000 00:01 12345  /dev/shm/ep_shm

# Shared file mapping:
7f3a20000000-7f3a20001000 rw-s 00000000 08:01 67890  /home/ravi/data.bin

# System V shared memory (no name shown):
7f3a30000000-7f3a30001000 rw-s 00000000 00:00 0

# Format: address range  perms  offset  dev  inode  name
# 's' in perms = shared mapping

The s flag in the permissions column means it is a shared mapping. The /dev/shm/ path confirms it is a POSIX shared memory object.

Interview Questions — Comparison

Q1. What are the advantages of POSIX shared memory over System V shared memory?

POSIX shared memory uses string names and file descriptors, integrating naturally with the UNIX I/O model. Standard tools like ls, stat, fchmod work on it. Its size can be changed dynamically using ftruncate(). System V uses a non-standard key/identifier scheme requiring separate commands (ipcs, ipcrm) and its own set of system calls. System V segment size is fixed at creation time.

Q2. When would you prefer a shared file mapping over POSIX shared memory?

When you need the data to persist across reboots. POSIX shared memory lives in tmpfs and is lost on reboot. A shared file mapping is backed by a real file on disk — the data survives a reboot. This is useful for things like persistent caches, configuration databases, or any data that should survive a system restart.

Q3. All shared memory techniques require external synchronization. Why?

The kernel provides no automatic locking for shared memory regions. If two processes write to overlapping offsets simultaneously without coordination, data corruption results. A semaphore, mutex (placed in the shared region itself, with PTHREAD_PROCESS_SHARED), or other synchronization primitive must be used to ensure only one writer at a time.

Q4. Why must you use offsets instead of pointers for references stored inside shared memory?

The kernel maps the same physical pages into each process’s virtual address space at a different virtual address. A pointer like 0x7f3a10001234 is valid in Process A but meaningless in Process B. An offset like “200 bytes from the start of the shared region” is the same in both processes because each can compute the absolute address as base_address + 200 using their own base.

Q5. What is the purpose of /proc/PID/maps and how does it relate to shared memory?

/proc/PID/maps lists every virtual memory region mapped by a process: its address range, permissions, offset, device, inode, and filename. All shared memory types appear here. Shared mappings are identified by the s flag in the permissions field. POSIX shared memory shows the /dev/shm/name path; System V shows no filename.

Q6. Can you resize a System V shared memory segment after creation?

No. The size is fixed at creation time via the size parameter of shmget(). To change the size, you must detach all processes, delete the segment with shmctl(IPC_RMID), and create a new one. POSIX shared memory and file-backed mappings allow dynamic resizing with ftruncate().

Leave a Reply

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