shm_open() โ€” Creating & Opening 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 *