shm_open(3)

πŸ’– shm_open(3) β€” the group chat edition πŸ’–

okay so bestie, we're in the Library Functions Manual and the star of the show is shm_open(3). Buckle up, this one's iconic πŸ‘‘


✨ NAME ✨

shm_open, shm_unlink β€” create/open or unlink POSIX shared memory objects 🌸


πŸ“š LIBRARY πŸ“š

Real-time library β€” that's librt, so link it with -lrt πŸ’…


πŸŽ€ SYNOPSIS πŸŽ€

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

int shm_open(const char *name, int oflag, mode_t mode);
int shm_unlink(const char *name);

πŸ¦‹ DESCRIPTION πŸ¦‹

So shm_open() creates and opens a brand-new POSIX shared memory object, or just opens one that already exists. No biggie. And a POSIX shared memory object? It's basically a handle that totally unrelated processes can use to mmap(2) the exact same region of shared memory. Besties sharing a vibe, literally. Meanwhile shm_unlink() does the opposite thing β€” it removes an object that some earlier shm_open() created. Converse operation, periodt.

The way shm_open() works is analogous to open(2). name says which shared memory object you're creating or opening. And for portable use, a shared memory object should be identified by a name of the form /somename β€” that's a null-terminated string of up to NAME_MAX (i.e., 255) characters, made of an initial slash followed by one or more characters, and none of those characters are slashes. Slash-free zone, no cap 🚫

oflag is a bit mask you get by ORing together exactly one of O_RDONLY or O_RDWR, plus any of the other flags listed right here:

  • O_RDONLY β€” open the object for read access. And heads up, an object opened this way can only be mmap(2)ed for read access (PROT_READ). Read-only main character energy.
  • O_RDWR β€” open the object for read-write access. Both. Greedy, we love it πŸ’…
  • O_CREAT β€” create the shared memory object if it doesn't already exist. Here's the tea: the user and group ownership of the object come from the corresponding effective IDs of the calling process, and the object's permission bits get set according to the low-order 9 bits of mode, except those bits set in the process file mode creation mask (peep umask(2)) get cleared for the new object. There's a set of macro constants you can use to define mode, and they're listed in open(2). (You get symbolic definitions of these constants by including <sys/stat.h>.)

A new shared memory object starts out with zero length β€” you set the size of the object with ftruncate(2). And the newly allocated bytes of a shared memory object are automatically initialized to 0. Fresh and clean ✨

  • O_EXCL β€” if O_CREAT was also specified, and a shared memory object with that name already exists, you get an error. And the check for the object's existence plus its creation if it doesn't exist are performed atomically. No race conditions in this house πŸ™…β€β™€οΈ
  • O_TRUNC β€” if the shared memory object already exists, truncate it to zero bytes. Gone. Zeroed. Slay.

You can get definitions of these flag values by including <fcntl.h>.

On successful completion, shm_open() returns a new file descriptor referring to the shared memory object. And it's guaranteed to be the lowest-numbered file descriptor not previously opened within the process. Number one priority, lowkey. The FD_CLOEXEC flag (see fcntl(2)) is set for the file descriptor too.

That file descriptor is normally used in subsequent calls to ftruncate(2) (for a newly created object) and mmap(2). And here's the cute part: after a call to mmap(2), the file descriptor may be closed without affecting the memory mapping. You can just let it go πŸ¦‹

Now shm_unlink() works analogous to unlink(2) β€” it removes a shared memory object name, and once all processes have unmapped the object, it deallocates and destroys the contents of the associated memory region. Bye forever. And after a successful shm_unlink(), attempts to shm_open() an object with the same name fail β€” unless O_CREAT was specified, in which case a new, distinct object gets created. Rebirth era πŸ’«


🌟 RETURN VALUE 🌟

On success, shm_open() returns a file descriptor (a nonnegative integer). On success, shm_unlink() returns 0. And on failure, both functions return -1 and set errno to indicate the error. Noted πŸ“


πŸ’€ ERRORS πŸ’€

  • EACCES β€” permission to shm_unlink() the shared memory object was denied. Rude.
  • EACCES β€” permission was denied to shm_open() name in the specified mode, or O_TRUNC was specified and the caller doesn't have write permission on the object.
  • EEXIST β€” both O_CREAT and O_EXCL were specified to shm_open() and the shared memory object specified by name already exists. It's already there, bestie.
  • EINVAL β€” the name argument to shm_open() was invalid. 🚩
  • EMFILE β€” the per-process limit on the number of open file descriptors has been reached. Too many open tabs, metaphorically.
  • ENAMETOOLONG β€” the length of name exceeds PATH_MAX. That name is a whole novel.
  • ENFILE β€” the system-wide limit on the total number of open files has been reached. The whole system is full, not just you 😭
  • ENOENT β€” an attempt was made to shm_open() a name that did not exist, and O_CREAT was not specified. Nothing there, sorry.
  • ENOENT β€” an attempt was to made to shm_unlink() a name that does not exist. (Yes, that typo-ish phrasing is straight from the original, we keep it fr fr.)

🌈 ATTRIBUTES 🌈

For the explanation of the terms used in this section, check attributes(7).

The original ships this as a table, but tables are not it in a terminal, so here it is as a bullet:
  • shm_open(), shm_unlink() β€” Thread safety: MT-Safe locale πŸ‘‘

⭐ VERSIONS ⭐

POSIX leaves the behavior of the combination of O_RDONLY and O_TRUNC unspecified. On Linux, this will successfully truncate an existing shared memory object β€” but this may not be so on other UNIX systems. So like, don't get too comfy πŸ”₯

The POSIX shared memory object implementation on Linux makes use of a dedicated tmpfs(5) filesystem that is normally mounted under /dev/shm. That's where all the magic lives πŸͺ„


🧚 STANDARDS 🧚

POSIX.1-2008. Certified, official, iconic πŸ’…


πŸ‘‘ HISTORY πŸ‘‘

glibc 2.2. POSIX.1-2001. Ancient lore, but make it fashion.

POSIX.1-2001 says that the group ownership of a newly created shared memory object is set to either the calling process's effective group ID or "a system default group ID".

POSIX.1-2008 says that the group ownership may be set to either the calling process's effective group ID or, if the object is visible in the filesystem, the group ID of the parent directory. Glow-up of a spec, honestly 🌸


πŸ’– EXAMPLES πŸ’–

okay bestie, so these two little programs are the whole vibe check: they use POSIX shared memory plus POSIX unnamed semaphores to pass a lil piece of data back and forth between each other. no third wheel, no drama. 🌸

The bounce program (which absolutely has to run first, no cap) raises the case of a string that the send program drops into the shared memory. Once the data's been glowed up, send prints out what the modified shared memory is holding. iconic. ✨

An example execution of the two programs goes a little something like this:

$ ./pshm_ucase_bounce /myshm &
[1] 270171
$ ./pshm_ucase_send /myshm hello;
HELLO

more deets about these programs are right below, don't worry 🫢

Program source: pshm_ucase.h

The following header file gets included by both programs below. Its main character purpose? Defining a structure that'll be imposed on the memory object that both programs are sharing. πŸ’…

#ifndef PSHM_UCASE_H
#define PSHM_UCASE_H

#include <err.h>
#include <semaphore.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>

#define BUF_SIZE 1024   /* Maximum size for exchanged string */

/* Define a structure that will be imposed on the shared
   memory object */

struct shmbuf {
    sem_t  sem1;            /* POSIX unnamed semaphore */
    sem_t  sem2;            /* POSIX unnamed semaphore */
    size_t cnt;             /* Number of bytes used in 'buf' */
    char   buf[BUF_SIZE];   /* Data being transferred */
};

#endif  // include guard

Program source: pshm_ucase_bounce.c

The bounce program creates a brand new shared memory object with the name given in its command-line argument, and it sizes that object to match the size of the shmbuf structure defined in the header file. then it maps the object into the process's address space and initializes two POSIX semaphores inside the object to 0. two semaphores, zeroed out, ready to be semaphore-ing. πŸŽ€

After the send program has posted the first of the semaphores, the bounce program upper cases the data that send placed in the memory, and then posts the second semaphore to tell send it may now access the shared memory. passing notes but the notes are memory. 😭

/* pshm_ucase_bounce.c

   Licensed under GNU General Public License v2 or later.
*/
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>

#include "pshm_ucase.h"

int
main(int argc, char *argv[])
{
    int            fd;
    char           *shmpath;
    struct shmbuf  *shmp;

    if (argc != 2) {
        fprintf(stderr, "Usage: %s /shm-path\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    shmpath = argv[1];

    /* Create shared memory object and set its size to the size
       of our structure.  */

    fd = shm_open(shmpath, O_CREAT | O_EXCL | O_RDWR, 0600);
    if (fd == -1)
        err(EXIT_FAILURE, "shm_open");

    if (ftruncate(fd, sizeof(struct shmbuf)) == -1)
        err(EXIT_FAILURE, "ftruncate");

    /* Map the object into the caller's address space.  */

    shmp = mmap(NULL, sizeof(*shmp), PROT_READ | PROT_WRITE,
                MAP_SHARED, fd, 0);
    if (shmp == MAP_FAILED)
        err(EXIT_FAILURE, "mmap");

    /* Initialize semaphores as process-shared, with value 0.  */

    if (sem_init(&shmp->sem1, 1, 0) == -1)
        err(EXIT_FAILURE, "sem_init-sem1");
    if (sem_init(&shmp->sem2, 1, 0) == -1)
        err(EXIT_FAILURE, "sem_init-sem2");

    /* Wait for 'sem1' to be posted by peer before touching
       shared memory.  */

    if (sem_wait(&shmp->sem1) == -1)
        err(EXIT_FAILURE, "sem_wait");

    /* Convert data in shared memory into upper case.  */

    for (size_t j = 0; j < shmp->cnt; j++)
        shmp->buf[j] = toupper((unsigned char) shmp->buf[j]);

    /* Post 'sem2' to tell the peer that it can now
       access the modified data in shared memory.  */

    if (sem_post(&shmp->sem2) == -1)
        err(EXIT_FAILURE, "sem_post");

    /* Unlink the shared memory object.  Even if the peer process
       is still using the object, this is okay.  The object will
       be removed only after all open references are closed.  */

    shm_unlink(shmpath);

    exit(EXIT_SUCCESS);
}

Program source: pshm_ucase_send.c

The send program takes two command-line arguments: the pathname of a shared memory object that the bounce program made earlier, and a string that's gonna get copied into that object. two args, one job. ✨

The program opens the shared memory object and maps it into its address space. then it copies the data specified in its second argument into the shared memory and posts the first semaphore, which tells bounce it can now access that data. after bounce posts the second semaphore, send prints the contents of the shared memory on standard output. chit chat, but make it computing. πŸ’…

/* pshm_ucase_send.c

   Licensed under GNU General Public License v2 or later.
*/
#include <fcntl.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

#include "pshm_ucase.h"

int
main(int argc, char *argv[])
{
    int            fd;
    char           *shmpath, *string;
    size_t         len;
    struct shmbuf  *shmp;

    if (argc != 3) {
        fprintf(stderr, "Usage: %s /shm-path string\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    shmpath = argv[1];
    string = argv[2];
    len = strlen(string);

    if (len > BUF_SIZE) {
        fprintf(stderr, "String is too long\n");
        exit(EXIT_FAILURE);
    }

    /* Open the existing shared memory object and map it
       into the caller's address space.  */

    fd = shm_open(shmpath, O_RDWR, 0);
    if (fd == -1)
        err(EXIT_FAILURE, "shm_open");

    shmp = mmap(NULL, sizeof(*shmp), PROT_READ | PROT_WRITE,
                MAP_SHARED, fd, 0);
    if (shmp == MAP_FAILED)
        err(EXIT_FAILURE, "mmap");

    /* Copy data into the shared memory object.  */

    shmp->cnt = len;
    memcpy(&shmp->buf, string, len);

    /* Tell peer that it can now access shared memory.  */

    if (sem_post(&shmp->sem1) == -1)
        err(EXIT_FAILURE, "sem_post");

    /* Wait until peer says that it has finished accessing
       the shared memory.  */

    if (sem_wait(&shmp->sem2) == -1)
        err(EXIT_FAILURE, "sem_wait");

    /* Write modified data in shared memory to standard output.  */

    if (write(STDOUT_FILENO, &shmp->buf, len) == -1)
        err(EXIT_FAILURE, "write");
    if (write(STDOUT_FILENO, "\n", 1) == -1)
        err(EXIT_FAILURE, "write");

    exit(EXIT_SUCCESS);
}

🌈 SEE ALSO 🌈

close(2), fchmod(2), fchown(2), fcntl(2), fstat(2), ftruncate(2), memfd_create(2), mmap(2), open(2), umask(2), shm_overview(7)


okay that's the whole page bestie, you made it to the end and I'm so proud of you πŸ₯ΉπŸ’– this one's from Linux man-pages 6.20, dated 2026-02-08. go forth and share memory responsibly, you absolute icon πŸ‘‘βœ¨

πŸ“¦ where this page comes from
  • arch rolling: man-pages 6.20-1 Β· BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • πŸ“œ the page's license: Linux-man-pages-copyleft
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms