shm_unlink() and Lifecycle Removing POSIX Shared Memory Objects and Managing Lifetime

 

shm_unlink() and Lifecycle
Removing POSIX Shared Memory Objects and Managing Lifetime
Step 4
of 4
cleanup
shm_unlink
persist
until reboot

The Lifetime of a Shared Memory Object

POSIX shared memory objects have kernel persistence: once created, they live until explicitly deleted or until the system reboots. They are not tied to the lifetime of the creating process. If your program crashes without calling shm_unlink(), the object stays in /dev/shm/.

Understanding the lifetime model is critical for writing correct, leak-free programs.

Full Lifecycle — From Creation to Destruction

shm_open(O_CREAT)
Object created in /dev/shm/ with size 0. Link count = 1.

ftruncate()
Size set. Kernel allocates backing pages (zero-filled).

mmap() × N processes
Each process gets a virtual address pointing to same pages. Map count increases.

shm_unlink()
Name removed from /dev/shm/. Existing mappings still valid. No new opens possible.

munmap() × N
When last mapping is removed (and fd count = 0), kernel frees the pages. Object is gone.

shm_unlink() — The Function
#include <sys/mman.h>

int shm_unlink(const char *name);
/* Returns: 0 on success, -1 on error */

shm_unlink() removes the shared memory object’s name from the filesystem. It works exactly like unlink() for files. The key behavior:

The name disappears immediately

No process can call shm_open("/myobj", ...) after shm_unlink("/myobj"). They will get ENOENT.

Existing mappings survive

Processes that already have the object mapped continue to work. The pages are freed only when the last mapping is removed.

/* Simple cleanup */
if (shm_unlink("/ep_shm") == -1) {
    perror("shm_unlink");
    /* ENOENT means it was already deleted — often OK to ignore */
}

A Useful Pattern: Unlink Right After Open

Because existing mappings survive shm_unlink(), you can unlink the name immediately after mmap(). This prevents orphaned objects if the program crashes later.

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

#define SHM_NAME "/ep_temp"
#define SHM_SIZE 4096

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

    fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
    if (fd == -1) { perror("shm_open"); exit(EXIT_FAILURE); }

    ftruncate(fd, SHM_SIZE);

    addr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE,
                MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); }

    close(fd);

    /*
     * Unlink name NOW — while we still have the mapping.
     * If this process crashes later, the object is auto-cleaned.
     * Other processes that already opened it are unaffected.
     */
    shm_unlink(SHM_NAME);

    /* Use shared memory normally ... */
    snprintf(addr, SHM_SIZE, "data from PID %d", (int)getpid());
    printf("addr contains: %s\n", addr);

    /* No need to shm_unlink at end — already done */
    munmap(addr, SHM_SIZE);
    return 0;
}

Tip: This “unlink early” pattern is especially useful when an object is private to one process (like a large working buffer) but you want the convenience of shm_open()/mmap(). The name is just a creation convenience — you don’t need it after the object is mapped.

Permissions and umask

Like open(), the mode argument to shm_open() is modified by the process’s umask:

/*
 * If umask is 0022:
 * Requested mode: S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH  (0644)
 * After umask:    0644 & ~0022 = 0644 & 0755 = 0644
 *
 * Requested mode: 0666
 * After umask:    0666 & ~0022 = 0644
 */

/* For private shared memory, use 0600 — unaffected by typical umask */
fd = shm_open("/myobj", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);

/* To share with group members, use 0660 */
fd = shm_open("/myobj", O_CREAT | O_RDWR,
              S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);

Check permissions after creation:

ls -la /dev/shm/myobj
# -rw------- 1 ravi ravi 4096 Jun 12 10:00 myobj
#   ↑
#   0600: owner read+write, nobody else

Inspecting and Removing Objects from Command Line

Because POSIX shared memory objects are files in /dev/shm/, you can manage them with standard shell commands:

# List all shared memory objects
ls -la /dev/shm/

# Check size and permissions
stat /dev/shm/myobj

# Remove a stale object (equivalent to shm_unlink)
rm /dev/shm/myobj

# Check if any process has the object mapped
lsof /dev/shm/myobj

# See all processes using shared memory
ls /proc/*/maps | xargs grep -l '/dev/shm'

Debugging tip: If your program crashes and leaves stale objects, use rm /dev/shm/yourname before rerunning. Otherwise the next run’s shm_open(O_CREAT | O_EXCL) will fail with EEXIST.

Object Persistence: POSIX vs Other IPC
IPC Object Persists after process exits? Persists after reboot? How to delete
POSIX Shared Memory Yes No (tmpfs) shm_unlink() or rm /dev/shm/name
System V Shared Memory Yes No shmctl(IPC_RMID) or ipcrm
Shared File Mapping Yes Yes (disk file) unlink() the backing file
Anonymous mmap (fork) No (dies with process) No munmap()
POSIX Message Queue Yes No mq_unlink()

Interview Questions — shm_unlink() and Lifecycle

Q1. Does shm_unlink() immediately free the shared memory pages?

No. shm_unlink() removes the name from /dev/shm/, but the underlying pages stay alive as long as any process has them mapped or any fd referencing the object is open. The pages are freed when the last reference drops — just like how a file is freed when its last open fd is closed and its link count reaches zero.

Q2. What is the difference between munmap() and shm_unlink()?

munmap() removes the mapping from the calling process’s virtual address space — other processes’ mappings are unaffected, and the object still exists. shm_unlink() removes the name of the object — no new process can open it, but existing mappings remain valid. Both are needed for complete cleanup.

Q3. What happens to data in shared memory when the creating process calls exit() without shm_unlink()?

The object persists in /dev/shm/ with all its data intact. Other processes can still open and map it. Only a reboot or explicit shm_unlink() (or rm /dev/shm/name) will remove it.

Q4. Can you call shm_unlink() before mmap()? Is that valid?

Yes, if you still hold the fd from shm_open(). You can call shm_unlink() after shm_open() and before mmap() — the fd still references the object. mmap(fd, ...) works fine. The name is just gone so no other process can open it by name. This is the “unlink early” pattern.

Q5. What error does shm_unlink() return if the object doesn’t exist?

It returns -1 with errno set to ENOENT. In cleanup code, it’s common to ignore this error since it means “already deleted.”

Q6. How would you implement an automatic cleanup of shared memory if a process crashes?

Three approaches: (1) Use the “unlink early” pattern — call shm_unlink() immediately after mmap(). (2) Install a signal handler for SIGTERM/SIGINT that calls shm_unlink(). (3) For daemons, use a dedicated cleanup process or a wrapper script. Note: SIGKILL cannot be caught — the “unlink early” pattern is the only reliable solution.

Leave a Reply

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