System V Shared Memory Limits, /proc, IPC_INFO & SHM_INFO

 

System V Shared Memory
Chapter 48 · Part 2 of 3 — Limits, /proc, IPC_INFO & SHM_INFO
Topic
SHM Limits
Level
Intermediate
Series
TLPI Ch 48

Why Do Limits Matter?

Linux puts hard upper bounds on shared memory so one application cannot exhaust all physical RAM with shared segments. If your program calls shmget() and gets ENOSPC or EINVAL, a limit has been hit. Knowing what the limits are and how to read or change them is a key skill for systems programming and embedded Linux work.

Key Terms

SHMMNI SHMMIN SHMMAX SHMALL SHMSEG /proc/sys/kernel IPC_INFO SHM_INFO shminfo shm_info

1. The Five Shared Memory Limits

Linux defines four tuneable limits for System V shared memory and one additional limit found on some other UNIX systems (but not implemented on Linux):

Limit Name What it Controls Scope Error if exceeded On Linux?
SHMMNI Max number of shared memory segments (IDs) on the system System-wide ENOSPC ✔ Yes
SHMMIN Minimum size (bytes) of a single segment; defined as 1 but effective minimum is page size Per-segment EINVAL ✔ Yes
SHMMAX Maximum size (bytes) of a single segment Per-segment EINVAL ✔ Yes
SHMALL Max total pages of shared memory across all segments System-wide ENOSPC ✔ Yes
SHMSEG Max attached segments per process Per-process EMFILE ✘ Not on Linux
Note: SHMMAX and SHMALL have practical upper limits determined by available RAM + swap space. Setting them higher than physical memory does not give you more memory.

2. Reading and Changing Limits via /proc

Linux exposes shared memory limits as virtual files under /proc/sys/kernel/. You can read them with cat and write new values with echo (as root) or persistently via /etc/sysctl.conf.

Limit /proc/sys/kernel/ file x86-32 ceiling value Typical default (2.6.31)
SHMMNI shmmni 32768 (IPCMNI) 4096
SHMMAX shmmax Depends on RAM 32 MB (varies)
SHMALL shmall Depends on RAM 2097152 pages
/* Reading /proc limits from a C program */
#include <stdio.h>
#include <stdlib.h>

static unsigned long read_proc_ulong(const char *path)
{
    FILE *f = fopen(path, "r");
    if (!f) { perror(path); return 0; }
    unsigned long val;
    fscanf(f, "%lu", &val);
    fclose(f);
    return val;
}

int main(void)
{
    printf("SHMMNI (max segments) : %lu\n",
        read_proc_ulong("/proc/sys/kernel/shmmni"));
    printf("SHMMAX (max seg size) : %lu bytes\n",
        read_proc_ulong("/proc/sys/kernel/shmmax"));
    printf("SHMALL (max pages)    : %lu pages\n",
        read_proc_ulong("/proc/sys/kernel/shmall"));
    return 0;
}
# Shell commands to read limits
cat /proc/sys/kernel/shmmni
cat /proc/sys/kernel/shmmax
cat /proc/sys/kernel/shmall

# Temporarily increase SHMMAX to 256 MB (as root)
echo 268435456 > /proc/sys/kernel/shmmax

# Persistent change via sysctl.conf
echo "kernel.shmmax = 268435456" >> /etc/sysctl.conf
sysctl -p

3. What Happens When Limits Are Exceeded

Each limit produces a specific errno value when breached. Knowing which limit caused the error helps you fix it.

Call errno Meaning Fix
shmget() ENOSPC SHMMNI or SHMALL exceeded Delete unused segments or raise the limit
shmget() EINVAL Requested size < SHMMIN or > SHMMAX Adjust size or raise SHMMAX via /proc
/* Detect and report limit-related errors */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main(void)
{
    /* Try to create a huge segment — likely to exceed SHMMAX */
    size_t huge = 1UL << 32;   /* 4 GB */
    key_t key   = ftok("/tmp", 'H');
    int shmid   = shmget(key, huge, IPC_CREAT | 0660);

    if (shmid == -1) {
        if (errno == EINVAL) {
            fprintf(stderr,
                "EINVAL: requested size exceeds SHMMAX "
                "or is below SHMMIN\n");
            fprintf(stderr,
                "Check: cat /proc/sys/kernel/shmmax\n");
        } else if (errno == ENOSPC) {
            fprintf(stderr,
                "ENOSPC: SHMMNI (too many segments) "
                "or SHMALL (total pages) limit hit\n");
            fprintf(stderr,
                "Run: ipcs -m   to list existing segments\n");
        } else {
            perror("shmget");
        }
        exit(1);
    }
    shmctl(shmid, IPC_RMID, NULL);
    return 0;
}

4. Reading All Limits at Once with IPC_INFO

Linux provides a non-standard shmctl() operation called IPC_INFO that fills a struct shminfo with the current values of all shared memory limits in one call. It is faster than reading each /proc file individually.

You pass 0 as the shmid argument because this operation is system-wide, not segment-specific. The third argument must be cast to (struct shmid_ds *) even though you pass a pointer to struct shminfo.

shminfo Field Corresponding Limit Description
shmmax SHMMAX Max segment size in bytes
shmmin SHMMIN Min segment size (always 1)
shmmni SHMMNI Max number of segments
shmseg SHMSEG Max attached per process (not enforced on Linux)
shmall SHMALL Max total shared memory pages
/* Use IPC_INFO to read all SHM limits */
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main(void)
{
    struct shminfo si;

    /*
     * shmid = 0 for system-wide operation.
     * Cast struct shminfo* to struct shmid_ds* as required by API.
     */
    if (shmctl(0, IPC_INFO, (struct shmid_ds *)&si) == -1) {
        perror("shmctl IPC_INFO");
        exit(1);
    }

    printf("SHMMAX (max segment bytes) : %lu\n", si.shmmax);
    printf("SHMMIN (min segment bytes) : %lu\n", si.shmmin);
    printf("SHMMNI (max segments)      : %lu\n", si.shmmni);
    printf("SHMSEG (max per-proc att.) : %lu\n", si.shmseg);
    printf("SHMALL (max total pages)   : %lu\n", si.shmall);

    /* Convert SHMALL pages to bytes */
    long page = sysconf(_SC_PAGESIZE);
    printf("SHMALL in bytes            : %lu\n",
           si.shmall * (unsigned long)page);
    return 0;
}

5. Current Usage with SHM_INFO

While IPC_INFO shows configured limits, the related SHM_INFO operation shows actual current usage — how many segments exist right now, how many pages are allocated, and how many are swapped. This is the runtime view as opposed to the configuration view.

The result is returned in a struct shm_info (note the underscore — different from shminfo).

shm_info Field Description
used_ids Number of shared memory segments currently in existence
shm_tot Total number of shared memory pages currently allocated
shm_rss Number of shared memory pages currently in physical RAM
shm_swp Number of shared memory pages currently swapped out
/* Use SHM_INFO to see current SHM usage */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main(void)
{
    struct shm_info si;   /* NOTE: shm_info, not shminfo */

    int maxidx = shmctl(0, SHM_INFO, (struct shmid_ds *)&si);
    if (maxidx == -1) {
        perror("shmctl SHM_INFO");
        exit(1);
    }

    long page = sysconf(_SC_PAGESIZE);

    printf("Segments in use       : %d\n",  si.used_ids);
    printf("Total pages allocated : %lu (%lu bytes)\n",
           si.shm_tot, si.shm_tot * (unsigned long)page);
    printf("Pages in RAM          : %lu\n",  si.shm_rss);
    printf("Pages swapped out     : %lu\n",  si.shm_swp);
    printf("Highest SHM index     : %d\n",  maxidx);

    return 0;
}
Tip: SHM_INFO returns the highest used index as its return value. You can combine it with SHM_STAT to walk through all existing segments — this is how tools like ipcs -m work internally.

6. Listing All Segments (like ipcs -m)

SHM_INFO returns the highest valid kernel table index. You can then iterate from 0 to that index, calling shmctl(i, SHM_STAT, &ds) on each. If index i has no segment, it returns -1 (skip it). This gives you the full list — exactly what ipcs -m does.

/* List all System V shared memory segments (like ipcs -m) */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <time.h>

int main(void)
{
    struct shm_info si;
    int maxidx = shmctl(0, SHM_INFO, (struct shmid_ds *)&si);
    if (maxidx == -1) { perror("SHM_INFO"); exit(1); }

    printf("%-10s %-12s %-10s %-8s %-6s\n",
           "shmid", "key", "size(B)", "owner", "nattch");
    printf("%s\n", "-------------------------------------------------------");

    for (int i = 0; i <= maxidx; i++) {
        struct shmid_ds ds;
        int shmid = shmctl(i, SHM_STAT, &ds);
        if (shmid == -1)
            continue;   /* slot unused */

        printf("%-10d 0x%-10lx %-10lu %-8d %-6lu\n",
               shmid,
               (unsigned long)ds.shm_perm.__key,
               (unsigned long)ds.shm_segsz,
               (int)ds.shm_perm.uid,
               (unsigned long)ds.shm_nattch);
    }
    return 0;
}

7. Practical: Hitting SHMMNI and Cleaning Up

In embedded or server environments you may have leftover shared memory segments from crashed processes. The ipcrm tool or a cleanup program using SHM_INFO + IPC_RMID can remove them.

/* Remove all shared memory segments owned by current user */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main(void)
{
    struct shm_info si;
    int maxidx = shmctl(0, SHM_INFO, (struct shmid_ds *)&si);
    if (maxidx == -1) { perror("SHM_INFO"); exit(1); }

    int removed = 0;
    uid_t my_uid = getuid();

    for (int i = 0; i <= maxidx; i++) {
        struct shmid_ds ds;
        int shmid = shmctl(i, SHM_STAT, &ds);
        if (shmid == -1) continue;

        if (ds.shm_perm.uid == my_uid) {
            if (shmctl(shmid, IPC_RMID, NULL) == 0) {
                printf("Removed shmid %d (size=%lu)\n",
                       shmid, (unsigned long)ds.shm_segsz);
                removed++;
            } else {
                perror("IPC_RMID");
            }
        }
    }
    printf("Total removed: %d segment(s)\n", removed);
    return 0;
}
# Shell equivalent for cleanup
# List all shm segments
ipcs -m

# Remove a specific segment by shmid
ipcrm -m <shmid>

# Remove ALL shared memory segments you own
ipcs -m | awk 'NR>3 {print $2}' | xargs -I{} ipcrm -m {}

Interview Questions & Answers

Q1. What are the four System V shared memory limits on Linux?
SHMMNI (max number of segments), SHMMIN (min size — effectively 1 page), SHMMAX (max size of one segment), and SHMALL (max total pages across all segments).

Q2. Which error does shmget() return when SHMMNI is exceeded?
ENOSPC. The same error is returned when SHMALL (total page pool) is exhausted. EINVAL is returned when the requested size violates SHMMIN or SHMMAX.

Q3. What is SHMSEG and is it enforced on Linux?
SHMSEG limits how many segments a single process can have attached simultaneously. It exists on some UNIX systems but is not implemented on Linux.

Q4. How do you change SHMMAX without rebooting?
Write the new value to /proc/sys/kernel/shmmax: echo 268435456 > /proc/sys/kernel/shmmax. For persistence across reboots, add kernel.shmmax = 268435456 to /etc/sysctl.conf and run sysctl -p.

Q5. Difference between IPC_INFO and SHM_INFO?
IPC_INFO returns the configured limits (SHMMAX, SHMMNI, etc.) in a struct shminfo. SHM_INFO returns actual current resource usage (how many segments exist, how many pages are allocated/swapped) in a struct shm_info.

Q6. What does SHM_INFO return as its return value?
The highest used index in the kernel’s shared memory table. You use this as the upper bound when iterating with SHM_STAT to enumerate all segments.

Q7. What is SHM_STAT and how does it differ from IPC_STAT?
Both retrieve a shmid_ds structure. IPC_STAT takes a real shmid as argument. SHM_STAT takes a kernel table index (0, 1, 2 …) and returns the shmid as its return value, making it suitable for iterating the entire table.

Q8. A production process crashed and left shared memory behind. How do you find and remove it?
Run ipcs -m to list segments. Note the nattch column — segments with 0 attachments are orphaned. Remove with ipcrm -m <shmid>, or write a program that uses SHM_INFO + SHM_STAT to iterate and calls IPC_RMID on each orphaned segment.

Q9. SHMALL is measured in pages, not bytes. Why?
Because shared memory is allocated in page-sized chunks. Measuring in pages avoids false precision and aligns with kernel memory management, which operates at page granularity.

Continue Learning

Next: Summary, best practices, relative addressing, and full producer-consumer example

Part 3 → Summary & Best Practices ← Part 1: shmid_ds

Leave a Reply

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