POSIX Shared Memory Overview & shm_open()

 

POSIX Shared Memory
Chapter 54 — Part 1: Overview & shm_open()
Topic
IPC via Shared Memory
Standard
POSIX / SUSv3
API
shm_open, mmap

What is POSIX Shared Memory?

POSIX shared memory is a mechanism that allows two or more processes to share a region of physical memory. It is one of the fastest forms of Inter-Process Communication (IPC) because data does not need to be copied between processes — they both map the same physical pages into their own virtual address space and read/write directly.

Unlike System V shared memory (the older API), POSIX shared memory uses a cleaner file-descriptor-based interface. A shared memory object is identified by a name (like /demo_shm) and behaves like a file that lives in RAM.

How POSIX Shared Memory Works

Two processes map the same shared memory object into their virtual address space:

Process A (Writer)
Virtual Address Space
Mapped Region
addr → physical page
memcpy(addr, data, len)
Physical RAM
/demo_shm
shared pages
Process B (Reader)
Virtual Address Space
Mapped Region
addr → same physical page
write(STDOUT, addr, len)

Both processes call shm_open() to get a file descriptor, then mmap() to map it into memory. After that, they access shared data directly through the pointer — no syscall needed per access.

Key Terms
shm_open() shm_unlink() mmap() ftruncate() fstat() MAP_SHARED O_CREAT O_RDWR /dev/shm kernel persistence munmap()

shm_open() — Opening or Creating a Shared Memory Object

shm_open() is the entry point. It creates a new shared memory object or opens an existing one. It returns a regular file descriptor.

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

int shm_open(const char *name, int oflag, mode_t mode);
/* Returns: file descriptor on success, -1 on error */
Parameter Description
name Object name — must start with /, e.g., /my_shm
oflag O_RDONLY, O_RDWR, optionally OR’d with O_CREAT, O_EXCL, O_TRUNC
mode Permission bits (like 0600), used only when O_CREAT is specified

Important flags:

  • O_CREAT — Create the object if it does not exist
  • O_EXCL — Combined with O_CREAT: fail if object already exists (atomic check+create)
  • O_RDWR — Open for both reading and writing
  • O_RDONLY — Open for reading only
  • O_TRUNC — Truncate the object to zero length if it already exists

On Linux, shared memory objects appear as files under /dev/shm/. The leading / in the name is stripped when creating the file there.

Basic Example: Creating a Shared Memory Object

This example creates a shared memory object named /myshm, sets its size to 4096 bytes, and maps it into the process address space:

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

#define SHM_NAME "/myshm"
#define SHM_SIZE 4096

int main(void)
{
    int fd;
    void *addr;

    /* Step 1: Create shared memory object */
    fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0600);
    if (fd == -1) {
        perror("shm_open");
        exit(EXIT_FAILURE);
    }

    /* Step 2: Set the size of the object */
    if (ftruncate(fd, SHM_SIZE) == -1) {
        perror("ftruncate");
        exit(EXIT_FAILURE);
    }

    /* Step 3: Map it into virtual address space */
    addr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    }

    /* Step 4: Use it — write a string */
    strncpy((char *)addr, "Hello from shared memory!", SHM_SIZE);
    printf("Written: %s\n", (char *)addr);

    /* Step 5: Unmap and close fd (object still exists!) */
    munmap(addr, SHM_SIZE);
    close(fd);

    printf("Shared memory object /dev/shm/myshm still exists.\n");
    printf("Run: cat /dev/shm/myshm\n");

    return 0;
}

Compile and run:

gcc -o shm_create shm_create.c -lrt
./shm_create
ls -l /dev/shm/

Note: Link with -lrt on older Linux systems. On modern glibc, it may not be needed.

Naming Rules for Shared Memory Objects
  • Name must begin with /
  • Name should contain only one / (at the start) for portability
  • Name cannot be / alone
  • On Linux: stored as a file in /dev/shm/

Valid names:

/my_shm
/demo_shm
/app123_buffer

Avoid (non-portable):

/dir/my_shm   /* Multiple slashes — not portable */

Lifecycle of a POSIX Shared Memory Object
Step Call Purpose
1 shm_open(name, O_CREAT|O_RDWR, 0600) Create or open the shared memory object; get fd
2 ftruncate(fd, size) Set the size (new objects start at zero bytes)
3 mmap(NULL, size, PROT, MAP_SHARED, fd, 0) Map it into the process address space
4 close(fd) Close fd (mapping stays active)
5 /* use addr directly */ Read/write via the mapped pointer
6 munmap(addr, size) Unmap from this process
7 shm_unlink(name) Remove the object (when no longer needed)

Kernel Persistence

SUSv3 mandates that POSIX shared memory objects have kernel persistence: they survive after all processes that created or mapped them have exited. The object remains in the system until:

  • A process explicitly calls shm_unlink(), or
  • The system is rebooted

This is different from anonymous mmap() memory, which disappears when the process exits. Because of kernel persistence, always remember to shm_unlink() when done to avoid resource leaks.

/* Always clean up! */
shm_unlink("/myshm");   /* Removes /dev/shm/myshm */

Example: Using O_CREAT | O_EXCL for Safe Creation

If two processes both try to create the same shared memory object, only one should succeed. Use O_EXCL for atomic check-and-create:

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

int main(void)
{
    int fd = shm_open("/exclusive_shm", O_CREAT | O_EXCL | O_RDWR, 0600);

    if (fd == -1) {
        if (errno == EEXIST) {
            printf("Shared memory already exists. Opening existing...\n");
            fd = shm_open("/exclusive_shm", O_RDWR, 0);
            if (fd == -1) {
                perror("shm_open existing");
                exit(EXIT_FAILURE);
            }
        } else {
            perror("shm_open");
            exit(EXIT_FAILURE);
        }
    } else {
        printf("Created new shared memory object.\n");
        ftruncate(fd, 1024);
    }

    /* ... use fd ... */
    close(fd);
    return 0;
}

POSIX Shared Memory vs System V Shared Memory
Feature POSIX System V
Identifier Name string (e.g., /myshm) Integer key (key_t)
Create API shm_open() shmget()
Map API mmap() shmat()
Unmap API munmap() shmdt()
Delete API shm_unlink() shmctl(IPC_RMID)
Interface style File descriptor (modern) Integer ID (older)
Visibility /dev/shm/ filesystem ipcs -m command

Interview Questions & Answers

Q1. What is POSIX shared memory and how does it differ from pipes or message queues?

POSIX shared memory allows multiple processes to map the same physical memory pages into their virtual address spaces. Unlike pipes or message queues, data does not pass through the kernel on each access — processes read and write directly to the mapped region. This makes it the fastest IPC mechanism, but it requires explicit synchronization (e.g., semaphores) since there is no built-in ordering or locking.

Q2. What does shm_open() return and what does it NOT do?

shm_open() returns a regular file descriptor referring to the shared memory object. It does NOT set the size of the object — a new object always starts with size zero. You must call ftruncate() to give it a non-zero size before mapping.

Q3. Why is ftruncate() necessary after shm_open(O_CREAT)?

A newly created shared memory object has size zero. Attempting to mmap() a zero-size object gives a zero-length mapping which is useless. ftruncate(fd, size) extends the object to the desired size, allocating the backing memory pages.

Q4. What is kernel persistence for shared memory?

Kernel persistence means the shared memory object survives after all processes that created or used it have exited. It remains until explicitly deleted with shm_unlink() or the system reboots. This is important to remember — forgetting to unlink causes resource leaks that persist across process restarts.

Q5. What is the purpose of O_EXCL in shm_open()?

When combined with O_CREAT, O_EXCL makes the creation atomic: the call succeeds only if the object does not already exist. If it does exist, shm_open() returns -1 with errno set to EEXIST. This prevents two processes from accidentally creating the same object simultaneously.

Q6. Where are POSIX shared memory objects stored on Linux?

On Linux, they are stored as files in the /dev/shm/ directory, which is a tmpfs filesystem backed by RAM. You can inspect them with ls -l /dev/shm/ and even use cat on them.

Q7. Can you use standard file operations (read, write, lseek) on the fd returned by shm_open()?

Yes, technically you can use read(), write(), lseek(), and ftruncate() on it since it is a regular file descriptor. However, the normal usage pattern is to call mmap() to get a pointer and then access memory directly, which avoids the overhead of system calls on each access.

Q8. What happens to existing mappings when shm_unlink() is called?

Existing mappings in all processes remain valid. shm_unlink() only removes the name from the filesystem, preventing new shm_open() calls from opening the object. Once all processes have called munmap() or exited, the underlying memory is freed. This is the same behavior as unlink() on regular files.

Continue Learning

Next: Writing data into shared memory with ftruncate and memcpy

Part 2: Writing to Shared Memory → EmbeddedPathashala Home

Leave a Reply

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