SHMOP(2)

๐Ÿ’– SHMOP(2) โ€” the <sys/shm.h> bestie era ๐Ÿ’–

okay so. this is the manual page for shmat and shmdt, from section 2 of the manual (system calls, iykyk). System V shared memory operations, babes. Let's get into it โœจ


๐ŸŒธ NAME ๐ŸŒธ

shmat, shmdt โ€” System V shared memory operations

๐ŸŽ€ LIBRARY ๐ŸŽ€

Standard C library (libc, <cyan>-lc`)

๐Ÿฆ‹ SYNOPSIS ๐Ÿฆ‹

#include <sys/shm.h>

void *shmat(int shmid, const void *_Nullable shmaddr, int shmflg);
int shmdt(const void *shmaddr);

๐Ÿ’… DESCRIPTION ๐Ÿ’…

โœจ shmat() โœจ

So shmat() attaches the System V shared memory segment that's identified by shmid onto the address space of the process that's calling it. literally moving in, no paperwork.

Where it lands is decided by shmaddr, and there are three (3) whole scenarios, pick your fighter:

  • If shmaddr is NULL, the system picks a suitable (unused) page-aligned address to attach the segment at. it's giving "you choose, king". ๐Ÿ‘‘
  • If shmaddr is not NULL and SHM_RND is specified in shmflg, the attach happens at the address equal to shmaddr rounded down to the nearest multiple of SHMLBA.
  • Otherwise, shmaddr must be a page-aligned address, and that's where the attach occurs. no rounding, no mercy. ๐Ÿ’€

On top of SHM_RND, you can also pass these flags in the shmflg bit-mask argument:

  • SHM_EXEC โ€” Linux-specific; since Linux 2.6.9 โ€” lets the contents of the segment be executed. the caller must have execute permission on the segment. main character energy fr fr ๐Ÿ”ฅ
  • SHM_RDONLY โ€” attach the segment for read-only access. the process must have read permission for the segment. if you don't pass this flag, the segment gets attached for read and write access, and the process must have read and write permission for the segment. and bestie, there is no notion of a write-only shared memory segment. it does not exist. periodt.
  • SHM_REMAP โ€” Linux-specific โ€” this one says the mapping of the segment should replace any existing mapping in the range starting at shmaddr and continuing for the size of the segment. (Normally you'd get an EINVAL error if a mapping already existed in that address range โ€” so this is you saying "yeah I know, do it anyway".) In this case, shmaddr must not be NULL. no cap.

The brk(2) value of the calling process is not altered by the attach. the segment will automatically be detached at process exit. and the same segment can be attached as a read one and as a read-write one, and more than once, in the process's address space. it contains multitudes ๐Ÿฆ‹

A successful shmat() call updates the members of the shmid_ds structure (see shmctl(2)) associated with the shared memory segment, like this:

  • shm_atime is set to the current time. โฐ
  • shm_lpid is set to the process-ID of the calling process.
  • shm_nattch is incremented by one.

โœจ shmdt() โœจ

shmdt() does the opposite vibe: it detaches the shared memory segment located at the address specified by shmaddr from the address space of the calling process. moving out, sending the goodbye text. ๐Ÿ˜ญ

The segment you're detaching must currently be attached with shmaddr equal to the value that the attaching shmat() call returned. like, you have to use the exact address it gave you, no improvising.

On a successful shmdt() call, the system updates the members of the shmid_ds structure associated with the shared memory segment, like this:

  • shm_dtime is set to the current time. โฐ
  • shm_lpid is set to the process-ID of the calling process.
  • shm_nattch is decremented by one. and if it becomes 0 and the segment is marked for deletion, the segment is deleted. gone, reduced to atoms ๐Ÿ’€โœจ

๐Ÿ’œ RETURN VALUE ๐Ÿ’œ

On success, shmat() returns the address of the attached shared memory segment. on error, it returns (void *) -1, and errno is set to indicate the error.

On success, shmdt() returns 0. on error, it returns -1, and errno is set to indicate the error. ๐Ÿ™ˆ


๐Ÿ”ฅ ERRORS ๐Ÿ”ฅ

shmat() can fail with one of the following errors:

  • EACCES โ€” the calling process doesn't have the required permissions for the requested attach type, and doesn't have the CAP_IPC_OWNER capability in the user namespace that governs its IPC namespace. permission denied, bestie, not your era ๐Ÿ’…
  • EIDRM โ€” shmid points to a removed identifier.
  • EINVAL โ€” invalid shmid value; or unaligned (i.e., not page-aligned and SHM_RND was not specified); or invalid shmaddr value; or can't attach the segment at shmaddr; or SHM_REMAP was specified and shmaddr was NULL. so many ways to be invalid, it's honestly iconic ๐Ÿ˜ญ
  • ENOMEM โ€” could not allocate memory for the descriptor or for the page tables.

shmdt() can fail with one of the following errors:

  • EINVAL โ€” there's no shared memory segment attached at shmaddr; or, shmaddr isn't aligned on a page boundary.

๐Ÿ‘‘ STANDARDS ๐Ÿ‘‘

POSIX.1-2008.

๐ŸŒˆ HISTORY ๐ŸŒˆ

POSIX.1-2001, SVr4.

In SVID 3 (or maybe even earlier), the type of the shmaddr argument was changed from char * into const void *, and the returned type of shmat() went from char * into void *. glow-up, honestly. โœจ

POSIX.1-2024 changed the error code from (void *) -1 to SHM_FAILED. the old way is giving vintage, the new way is giving modern.


๐Ÿงš NOTES ๐Ÿงš

  • After a fork(2), the child inherits the attached shared memory segments. family heirloom ๐Ÿซถ
  • After an execve(2), all attached shared memory segments are detached from the process.
  • Upon _exit(3), all attached shared memory segments are detached from the process.

Using shmat() with shmaddr equal to NULL is the preferred, portable way of attaching a shared memory segment. but be aware: the shared memory segment attached this way may end up attached at different addresses in different processes. so any pointers you keep inside that shared memory have to be made relative (typically to the starting address of the segment), rather than absolute. do not hardcode addresses, I am begging ๐Ÿ™

On Linux, it's possible to attach a shared memory segment even if it's already marked to be deleted. however, POSIX.1 does not specify this behavior, and many other implementations don't support it. so like, it's a Linux thing, don't build your whole personality on it.

The following system parameter affects shmat():

  • SHMLBA โ€” Segment low boundary address multiple. When you're explicitly specifying an attach address in a call to shmat(), the caller should make sure the address is a multiple of this value. This is necessary on some architectures, either to ensure good CPU cache performance, or to ensure that different attaches of the same segment have consistent views within the CPU cache. SHMLBA is normally some multiple of the system page size. (On many Linux architectures, SHMLBA is the same as the system page size.)

The implementation places no intrinsic per-process limit on the number of shared memory segments (SHMSEG). go off, attach as many as your heart desires ๐Ÿ’–


โญ and that's part 1, bestie โ€” hold on, there's more coming, don't go anywhere โญ

โœจ EXAMPLES โœจ

okay bestie this is the FUN part ๐Ÿ˜ญ๐Ÿ’– two lil baby programs passing a string back and forth through a shared memory segment, like besties swapping notes in class but the note is literally bytes in kernel memory. Details below, but first โ€” a shell session showing the whole thing in action โœจ

In one terminal window, we run the "reader" program, which creates a System V shared memory segment and a System V semaphore set. It prints out the IDs of the objects it made, then justโ€ฆ waits ๐Ÿซถ waits for the semaphore to change value. main character behaviour honestly.

$ ./svshm_string_read;
shmid = 1114194
semid = 15

In another terminal window, we run the "writer" program. She takes three command-line arguments: the IDs of the shared memory segment and semaphore set created by the "reader", plus a string. She attaches the existing shared memory segment, copies the string into shared memory, and modifies the semaphore value. slay ๐Ÿ’…

$ ./svshm_string_write 1114194 15 'Hello, world';

Now back to the terminal where the "reader" is vibing โ€” we see she's stopped waiting on the semaphore and has printed the string that the writer copied into the shared memory segment:

Hello, world

๐ŸŒธ Program source: svshm_string.h ๐ŸŒธ

This header file gets included by both the "reader" and "writer" programs, no cap:

/* svshm_string.h

   Licensed under GNU General Public License v2 or later.
*/
#ifndef SVSHM_STRING_H
#define SVSHM_STRING_H

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/sem.h>

union semun {                   /* Used in calls to semctl() */
    int                 val;
    struct semid_ds     *buf;
    unsigned short      *array;
#if defined(__linux__)
    struct seminfo      *__buf;
#endif
};

#define MEM_SIZE 4096

#endif  // include guard

๐Ÿฆ‹ Program source: svshm_string_read.c ๐Ÿฆ‹

The "reader" program creates a shared memory segment and a semaphore set containing one semaphore. Then she attaches the shared memory object into her address space and initializes the semaphore value to 1. Finally โ€” the iconique part โ€” she waits for the semaphore value to become 0, and afterwards prints the string that the "writer" copied into the shared memory segment. patience is a flex fr fr ๐Ÿ˜Œ

/* svshm_string_read.c

   Licensed under GNU General Public License v2 or later.
*/
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>

#include "svshm_string.h"

int
main(void)
{
    int            semid, shmid;
    char           *addr;
    union semun    arg, dummy;
    struct sembuf  sop;

    /* Create shared memory and semaphore set containing one
       semaphore.  */

    shmid = shmget(IPC_PRIVATE, MEM_SIZE, IPC_CREAT | 0600);
    if (shmid == -1)
        err(EXIT_FAILURE, "shmget");

    semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
    if (semid == -1)
        err(EXIT_FAILURE, "semget");

    /* Attach shared memory into our address space.  */

    addr = shmat(shmid, NULL, SHM_RDONLY);
    if (addr == (void *) -1)
        err(EXIT_FAILURE, "shmat");

    /* Initialize semaphore 0 in set with value 1.  */

    arg.val = 1;
    if (semctl(semid, 0, SETVAL, arg) == -1)
        err(EXIT_FAILURE, "semctl");

    printf("shmid = %d\n", shmid);
    printf("semid = %d\n", semid);

    /* Wait for semaphore value to become 0.  */

    sop.sem_num = 0;
    sop.sem_op = 0;
    sop.sem_flg = 0;

    if (semop(semid, &sop, 1) == -1)
        err(EXIT_FAILURE, "semop");

    /* Print the string from shared memory.  */

    printf("%s\n", addr);

    /* Remove shared memory and semaphore set.  */

    if (shmctl(shmid, IPC_RMID, NULL) == -1)
        err(EXIT_FAILURE, "shmctl");
    if (semctl(semid, 0, IPC_RMID, dummy) == -1)
        err(EXIT_FAILURE, "semctl");

    exit(EXIT_SUCCESS);
}

๐ŸŽ€ Program source: svshm_string_write.c ๐ŸŽ€

The writer program takes three command-line arguments: the IDs of the shared memory segment and semaphore set that were already created by the "reader", plus a string. She attaches the shared memory segment into her address space, and then decrements the semaphore value to 0 to let the "reader" know that she can now go examine the contents of shared memory. the way that she justโ€ฆ signals. communication queen ๐Ÿ‘‘

/* svshm_string_write.c

   Licensed under GNU General Public License v2 or later.
*/
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sem.h>
#include <sys/shm.h>

#include "svshm_string.h"

int
main(int argc, char *argv[])
{
    int            semid, shmid;
    char           *addr;
    size_t         size;
    struct sembuf  sop;

    if (argc != 4) {
        fprintf(stderr, "Usage: %s shmid semid string\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    size = strlen(argv[3]) + 1;  /* +1 to include trailing '\0' */
    if (size > MEM_SIZE) {
        fprintf(stderr, "String is too big!\n");
        exit(EXIT_FAILURE);
    }

    /* Get object IDs from command-line.  */

    shmid = atoi(argv[1]);
    semid = atoi(argv[2]);

    /* Attach shared memory into our address space and copy string
       (including trailing null byte) into memory.  */

    addr = shmat(shmid, NULL, 0);
    if (addr == (void *) -1)
        err(EXIT_FAILURE, "shmat");

    memcpy(addr, argv[3], size);

    /* Decrement semaphore to 0.  */

    sop.sem_num = 0;
    sop.sem_op = -1;
    sop.sem_flg = 0;

    if (semop(semid, &sop, 1) == -1)
        err(EXIT_FAILURE, "semop");

    exit(EXIT_SUCCESS);
}

๐Ÿ‘€ SEE ALSO ๐Ÿ‘€

go read these next bestie, the lore is DEEP:


and that's the whole page bestie!! ๐Ÿซถโœจ this one came from Linux man-pages 6.20, dated 2026-06-05, and it's page SHMOP(2) in case you need to cite it in your essay ๐Ÿ’…๐Ÿ“š go share some memory responsibly, drink water, and don't forget to shmdt() before you leave. mwah ๐Ÿ’–๐ŸŒˆ

๐Ÿ“ฆ 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