Linux Shared Memory Tutorial: POSIX and System V Examples in C

Linux shared memory lets two or more processes read and write the same block of RAM, with no copying through pipes or sockets in between. That makes it the fastest way for processes on one machine to talk. Below are two complete C programs, a writer and a reader, that compile and run as-is, plus the System V version, a POSIX vs System V comparison, and the cleanup commands you’ll need when a segment gets stuck.

I compiled and ran every example on this page on Ubuntu 24.04 with GCC 13. The output shown is the real output.

The Short Version

  • New code: use POSIX shared memory, shm_open plus mmap. It shows up as a file in /dev/shm.
  • Old code and tutorials use System V: shmget, shmat, shmdt, shmctl. It still works fine.
  • Shared memory has no built-in locking. If more than one process writes, add a semaphore.
  • Segments outlive your program. Always clean up with shm_unlink or shmctl(IPC_RMID).

Understanding Shared Memory

Normally every process has its own private memory. The kernel makes sure process A can’t see process B’s data. Shared memory is the deliberate exception: the kernel maps the same physical pages into both processes, so a write by one is instantly visible to the other.

Why Use Shared Memory

Speed. Pipes, sockets and message queues all copy data through the kernel. Shared memory doesn’t copy at all after setup. The tradeoff is that you’re now responsible for coordination, which is where most bugs come from.

POSIX vs System V Shared Memory

Linux has two APIs for the same idea. Here’s how they compare:

POSIXSystem V
Create / openshm_open()shmget()
Attachmmap()shmat()
Detachmunmap()shmdt()
Deleteshm_unlink()shmctl(id, IPC_RMID, NULL)
Identified byA name like /my_shmA numeric key, usually from ftok()
Where you see itls /dev/shmipcs -m
Pick it forNew codeExisting code that already uses it

My take: use POSIX for anything new. The API is file-like, which is easier to reason about, and you can inspect it with plain ls.

Getting Started with Linux Shared Memory (POSIX)

The writer creates a 4 KB shared segment, maps it into its own address space, and writes a string into it. Save it as writer.c:

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

#define SHM_NAME "/ceos3c_demo"
#define SHM_SIZE 4096

int main(void) {
    int fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0600);
    if (fd == -1) { perror("shm_open"); return 1; }

    if (ftruncate(fd, SHM_SIZE) == -1) { perror("ftruncate"); return 1; }

    char *mem = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (mem == MAP_FAILED) { perror("mmap"); return 1; }

    snprintf(mem, SHM_SIZE, "Hello from PID %d", getpid());
    printf("Wrote: %s\n", mem);

    munmap(mem, SHM_SIZE);
    close(fd);
    return 0;
}Code language: PHP (php)

Reading Shared Memory

The reader opens the same name read-only, maps it, prints what’s there, and then removes the segment. Save it as reader.c:

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

#define SHM_NAME "/ceos3c_demo"
#define SHM_SIZE 4096

int main(void) {
    int fd = shm_open(SHM_NAME, O_RDONLY, 0);
    if (fd == -1) { perror("shm_open"); return 1; }

    char *mem = mmap(NULL, SHM_SIZE, PROT_READ, MAP_SHARED, fd, 0);
    if (mem == MAP_FAILED) { perror("mmap"); return 1; }

    printf("Read: %s\n", mem);

    munmap(mem, SHM_SIZE);
    close(fd);
    shm_unlink(SHM_NAME);   /* remove it once we're done */
    return 0;
}Code language: PHP (php)

Compile and Run

gcc -Wall -o writer writer.c
gcc -Wall -o reader reader.c
./writer
ls -l /dev/shm
./reader

Here’s the output on my test box:

$ ./writer
Wrote: Hello from PID 463

$ ls -l /dev/shm
-rw------- 1 root root 4096 Oct  5 15:47 ceos3c_demo

$ ./reader
Read: Hello from PID 463

$ ./reader
shm_open: No such file or directoryCode language: JavaScript (javascript)

The second ./reader fails on purpose. The first run called shm_unlink, so the segment is gone. That’s the cleanup working.

On older systems (glibc before 2.34) you’ll get an undefined reference to shm_open. Add -lrt to the gcc line to fix it.

The System V Version

Same idea with the older API. You’ll see this in a lot of existing code and university courses. Writer:

#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define SHM_SIZE 1024

int main(void) {
    key_t key = ftok("/tmp", 'C');
    int shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0600);
    if (shmid == -1) { perror("shmget"); return 1; }

    char *mem = shmat(shmid, NULL, 0);
    if (mem == (char *) -1) { perror("shmat"); return 1; }

    strcpy(mem, "Hello from System V shared memory");
    printf("Wrote to segment %d\n", shmid);

    shmdt(mem);
    return 0;
}Code language: PHP (php)

Reader:

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define SHM_SIZE 1024

int main(void) {
    key_t key = ftok("/tmp", 'C');
    int shmid = shmget(key, SHM_SIZE, 0600);
    if (shmid == -1) { perror("shmget"); return 1; }

    char *mem = shmat(shmid, NULL, SHM_RDONLY);
    if (mem == (char *) -1) { perror("shmat"); return 1; }

    printf("Read: %s\n", mem);

    shmdt(mem);
    shmctl(shmid, IPC_RMID, NULL);   /* delete the segment */
    return 0;
}Code language: PHP (php)
$ ./sysv_writer
Wrote to segment 0

$ ipcs -m
------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x43000035 0          root       600        1024       0

$ ./sysv_reader
Read: Hello from System V shared memoryCode language: JavaScript (javascript)

ftok("/tmp", 'C') turns a path and a character into a key both programs agree on. The path has to exist, and both processes must use the same path and character.

Key Things to Remember

Memory Management Tips

  • Segments don’t disappear when your program exits. If nobody calls shm_unlink or IPC_RMID, they stay until reboot.
  • Use permissions like 0600 unless other users really need access.
  • Shared memory counts as shared in free -h, and on most systems /dev/shm is capped at half your RAM.

Keeping Things in Sync

The examples above are safe only because one process writes and then the other reads. If two processes write at the same time, you get torn, half-written data. The usual fix is a named POSIX semaphore around every access:

sem_t *lock = sem_open("/ceos3c_lock", O_CREAT, 0600, 1);

sem_wait(lock);      /* take the lock */
/* read or write shared memory here */
sem_post(lock);      /* release it */

sem_close(lock);Code language: JavaScript (javascript)

Remove it with sem_unlink("/ceos3c_lock") when you’re done, same as the memory segment.

Checking on Shared Memory

# POSIX segments
ls -l /dev/shm

# System V segments
ipcs -m

# details for one System V segment
ipcs -m -i <shmid>Code language: PHP (php)

Common Problems and Fixes

ErrorCauseFix
shm_open: No such file or directoryReader ran before the writer, or the segment was already unlinkedRun the writer first. Check ls /dev/shm
shm_open: Permission deniedCreated by another user with 0600Run as the same user, or create it with looser permissions
undefined reference to shm_openOld glibcLink with -lrt
shmget: Invalid argumentSegment exists with a smaller size than you asked forRemove it with ipcrm -m <shmid> and recreate
Segments piling upPrograms crash before cleanuprm /dev/shm/name for POSIX, ipcrm -m <shmid> for System V

Real-World Uses

PostgreSQL keeps its shared buffer cache in shared memory. Chrome and Firefox use it to move data between their many processes. Audio and video pipelines use it to hand big buffers around without copying. If you’re moving lots of data between processes on one machine and a socket is too slow, this is the tool.

FAQ

Is shared memory faster than pipes?

Yes, for large data. Pipes copy every byte through the kernel. Shared memory copies nothing after setup. For small messages the difference rarely matters.

Where is shared memory stored in Linux?

In RAM. POSIX segments appear as files in /dev/shm, which is a tmpfs filesystem backed by memory, not disk.

Does shared memory survive a reboot?

No. It lives in RAM and is gone after a restart.

Making It Work Better

Start with the POSIX writer and reader above, add a semaphore the moment a second writer shows up, and always clean up. If you want the bigger picture of how Linux handles RAM, cache and swap, read my Linux memory management guide. The shm_overview man page is the reference.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Share via
Copy link
Powered by Social Snap