shm_open() in Linux: Creating & Opening POSIX Shared Memory

shm_open() — Creating & Opening Shared Memory
Chapter 54 — Part 2 of 5 | The Linux Programming Interface
Step 1
of 2-Step Process
int fd
Return Value
-lrt
Link Flag

📚 Chapter 54 Series
Part 1: Introduction & Overview Part 2: shm_open() Part 3: ftruncate() & fstat() Part 4: mmap() with POSIX shm Part 5: shm_unlink() & Examples

🔑 Key Concepts in This Part
shm_open() O_CREAT O_EXCL O_RDONLY O_RDWR O_TRUNC Naming Rules mode_t permissions FD_CLOEXEC Effective UID/GID

Function Signature

The shm_open() function is declared in <sys/mman.h>:

#include <fcntl.h>       /* Defines O_* constants */
#include <sys/stat.h>    /* Defines mode constants */
#include <sys/mman.h>

int shm_open(const char *name, int oflag, mode_t mode);
/* Returns: file descriptor on success, -1 on error */

The three arguments are deliberately modeled after open(): a name, flags, and permissions. If you already know how to call open(), you mostly know how to call shm_open().

📌 Argument 1: name — Naming Rules

The name argument identifies the shared memory object. POSIX.1 defines portable naming rules that you should always follow:

✅ Correct Names
/myshm
/ep_shared_buf
/demo_shm
Must start with a single /, followed by one or more characters with no additional /
❌ Incorrect Names
myshm
/my/shm
//shm
Missing leading /, or contains additional / characters in the name
💡 Linux Behavior: On Linux the name becomes a file directly under /dev/shm/. So /myshm appears as /dev/shm/myshm. You can verify this with ls -l /dev/shm after creation.

🚩 Argument 2: oflag — Bit Flags

The oflag argument is a bitmask that controls two things: (a) whether you are creating a new object or opening an existing one, and (b) the access mode. You must specify exactly one access mode flag.

Flag Category Meaning
O_CREAT Creation Create the object if it doesn’t exist. If it exists, just open it (no error).
O_EXCL Creation Used with O_CREAT: fail with EEXIST if object already exists. Guarantees you are the creator.
O_RDONLY Access (required) Open for read-only access. The resulting mmap() can only use PROT_READ.
O_RDWR Access (required) Open for read-write access. Required if the process will write to shared memory.
O_TRUNC Modification Truncate the object to zero length on open (if the object already exists). Use carefully.

/* Common flag combinations */
O_CREAT | O_RDWR // Create if not exist, open for R/W (most common for writer/creator)
O_CREAT | O_EXCL | O_RDWR // Create exclusively (fail if exists) — safe for single creator
O_RDONLY // Open existing object for read-only (typical for reader process)
O_RDWR // Open existing object for read-write (without creating)

🔐 Argument 3: mode — Permissions

The mode argument specifies the file permission bits when creating a new object. It is exactly the same as the mode bits used for regular files (see stat(2)).

If you are not creating a new object (i.e., O_CREAT is absent from oflag), you should pass 0 for mode — it is ignored in that case.

/* Common permission values for shm_open() */

fd = shm_open("/myshm", O_CREAT | O_RDWR, 0600);
/*                                          ^^^^
 *  0600 = owner read + write
 *  0660 = owner + group read + write
 *  0644 = owner read+write, others read-only
 *
 * The actual permissions are further masked by the
 * process's umask, just like open().
 */

/* Opening an existing object (no creation) */
fd = shm_open("/myshm", O_RDONLY, 0);
/*                                ^ pass 0 when not creating */
Ownership: When a new shared memory object is created, its owner and group are set from the effective user ID and group ID of the calling process (same rule as creating a file with open()).

🔒 FD_CLOEXEC — Automatic Close on exec()

The file descriptor returned by shm_open() always has the FD_CLOEXEC flag set automatically. This means:

What it means

If the process calls exec() to replace its image with a new program, the shared memory file descriptor is automatically closed. The new program does not inherit it.

Why it matters

This is consistent with the behavior of memory mappings: exec() unmaps all memory mappings of the old process. So there is no point in the new program having the fd. This prevents accidental fd leaks into child processes.

💻 Code Example: shm_open() — Create vs Open

This example shows two processes: a creator (uses O_CREAT | O_EXCL) and a consumer (opens existing object with O_RDONLY).

/*
 * shm_creator.c — Creates a shared memory object exclusively
 * Compile: gcc shm_creator.c -o shm_creator -lrt
 */

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

#define SHM_NAME  "/ep_channel"
#define SHM_SIZE  1024

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

    /*
     * O_CREAT | O_EXCL | O_RDWR:
     *   - Create the object (O_CREAT)
     *   - Fail if already exists (O_EXCL) — we want to be the only creator
     *   - Open for read-write (O_RDWR)
     * 0660: owner and group can read/write
     */
    fd = shm_open(SHM_NAME, O_CREAT | O_EXCL | O_RDWR, 0660);
    if (fd == -1) {
        if (errno == EEXIST) {
            fprintf(stderr,
                "Shared memory '%s' already exists. "
                "Run: shm_unlink %s or rm /dev/shm%s\n",
                SHM_NAME, SHM_NAME, SHM_NAME);
        } else {
            perror("shm_open");
        }
        exit(EXIT_FAILURE);
    }

    printf("[Creator] shm_open() OK. fd=%d\n", fd);

    /* Set the size before any mmap() */
    if (ftruncate(fd, SHM_SIZE) == -1) {
        perror("ftruncate");
        shm_unlink(SHM_NAME);
        exit(EXIT_FAILURE);
    }

    /* Map it */
    addr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED) {
        perror("mmap");
        shm_unlink(SHM_NAME);
        exit(EXIT_FAILURE);
    }
    close(fd);  /* fd not needed after mmap */

    /* Write a message */
    snprintf(addr, SHM_SIZE, "Message from creator: pid=%d", (int)getpid());
    printf("[Creator] Wrote: %s\n", addr);
    printf("[Creator] Run shm_reader now. Press Enter to cleanup...\n");
    getchar();

    munmap(addr, SHM_SIZE);
    shm_unlink(SHM_NAME);
    printf("[Creator] Cleaned up.\n");
    return 0;
}
/*
 * shm_reader.c — Opens an existing shared memory object read-only
 * Compile: gcc shm_reader.c -o shm_reader -lrt
 * Run AFTER shm_creator is running.
 */

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

#define SHM_NAME  "/ep_channel"
#define SHM_SIZE  1024

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

    /*
     * O_RDONLY: open existing object for reading only
     * mode = 0: not creating, so mode is ignored
     */
    fd = shm_open(SHM_NAME, O_RDONLY, 0);
    if (fd == -1) {
        perror("shm_open");
        exit(EXIT_FAILURE);
    }
    printf("[Reader] shm_open() OK. fd=%d\n", fd);

    /* Map read-only: PROT_READ only (matching O_RDONLY) */
    addr = mmap(NULL, SHM_SIZE, PROT_READ, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    }
    close(fd);

    printf("[Reader] Read: %s\n", addr);

    munmap(addr, SHM_SIZE);
    return 0;
}

How to test:

# Terminal 1
gcc shm_creator.c -o shm_creator -lrt
./shm_creator

# Terminal 2 (while creator is waiting)
gcc shm_reader.c -o shm_reader -lrt
./shm_reader
# Press Enter in Terminal 1 to cleanup

⚠️ O_TRUNC — Use With Caution

Specifying O_TRUNC when opening an existing shared memory object truncates it to zero length immediately. This destroys any existing data.

/* O_TRUNC: opens the existing object and resets its size to 0 */
fd = shm_open("/myshm", O_RDWR | O_TRUNC, 0);
/*
 * After this call:
 *   - The object exists but has size 0
 *   - Any processes that had it mapped still have their mapping
 *     but the backing region was shrunk — accessing beyond the
 *     new end results in SIGBUS
 *   - You must call ftruncate() again to restore a usable size
 *
 * Linux note: O_TRUNC works even with O_RDONLY (read-only open),
 * but this is undefined behavior per POSIX — avoid it.
 */
⚠️ Portability Warning: SUSv3 says the behavior of O_TRUNC combined with O_RDONLY is undefined. On Linux it truncates anyway, but do not rely on this in portable code.

🎯 Interview Questions — shm_open()
Q1. What are the POSIX portable naming rules for POSIX shared memory objects?
Answer: The name must begin with a single forward slash (/), followed by one or more characters, none of which are additional /. So /myshm is valid, but myshm (no leading slash) and /my/shm (extra slash) are not portably valid.
Q2. What is the difference between O_CREAT alone and O_CREAT | O_EXCL?
Answer: O_CREAT alone creates the object if it does not exist; if it already exists, it simply opens it (no error). O_CREAT | O_EXCL guarantees the caller is the creator — if the object already exists, the call fails with EEXIST. Use O_EXCL when you need exclusive ownership of the creation.
Q3. Why does shm_open() set FD_CLOEXEC on the returned file descriptor?
Answer: Because when a process calls exec(), all memory mappings are unmapped. Keeping the file descriptor open in the new program’s image would be useless at best and a resource leak at worst. Setting FD_CLOEXEC ensures automatic closure, preventing accidental inheritance by exec’d children.
Q4. What should you pass for the mode argument when opening an existing shared memory object?
Answer: Pass 0. The mode argument is only used when O_CREAT is in oflag. When you are only opening an existing object, the mode value is ignored, so passing 0 makes the intent clear.
Q5. Can a read-only mapped process and a read-write mapped process share the same POSIX shared memory object simultaneously?
Answer: Yes. One process opens with O_RDONLY and maps with PROT_READ; another opens with O_RDWR and maps with PROT_READ | PROT_WRITE. Both refer to the same underlying object. The writer’s changes are immediately visible to the reader because both map the same physical pages (via MAP_SHARED).
Q6. What header files are needed to use shm_open()?
Answer: You need: <fcntl.h> (for O_CREAT, O_RDWR, etc.), <sys/stat.h> (for mode constants like S_IRUSR), and <sys/mman.h> (for the shm_open() prototype itself).
Q7. What error does shm_open() return if you specify O_CREAT | O_EXCL for an object that already exists?
Answer: It returns -1 and sets errno to EEXIST (error: “File exists”). Your code should check for this specific error if you want to handle the “already created by another process” case gracefully.

Continue the Series
Next: ftruncate() and fstat() — sizing and inspecting shared memory objects

← Part 1: Introduction Part 3: ftruncate() & fstat() →

Leave a Reply

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