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.
Despite their differences, all three shared memory techniques share the same fundamental properties:
All provide the fastest possible IPC — data lives in RAM and is accessed directly via pointer dereference. No kernel copy happens during data exchange.
None provide built-in synchronization. Applications must use a semaphore (POSIX or System V) or mutex to coordinate access — otherwise concurrent writes corrupt data.
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.
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.
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 withftruncate() |
Resizable withftruncate() |
| 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 |
The choice comes down to one key question: do you need data to survive a reboot?
open() + mmap()
Data written to real disk file
shm_open() + mmap()
Pure RAM, no disk overhead
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:
/* 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);
/* 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");
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.
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).
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.
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().
Continue Learning
← shm_unlink() Next: Complete Writer/Reader Example → Chapter Index
