What is /proc/PID/maps?
Every Linux process has its own virtual address space. This space is divided into segments β executable code, data, stack, shared libraries, memory mappings, and shared memory. The kernel exposes a complete view of this layout through a special file: /proc/PID/maps, where PID is the process ID.
For System V shared memory, this file is extremely useful. You can see exactly where a segment is attached in virtual memory, what permissions it has, and whether it has been marked for deletion. Understanding this file is fundamental to debugging IPC issues.
Key Terms in This Topic
Each line in the file represents one memory segment. There are exactly six columns on every line.
| # | Column Name | Example | Meaning |
|---|---|---|---|
| 1 | Address range | b7e40000-b7e41000 | Start and end virtual addresses in hex. The end address is the first byte after the segment. |
| 2 | Permissions | rw-s | r=read, w=write, x=execute, hyphen=disabled. Last char: p=private (copy-on-write), s=shared. System V shm is always ‘s’. |
| 3 | Offset | 00000000 | Byte offset into the backing file. For System V shm this is always 0. |
| 4 | Device | 00:06 | Major:minor device number of the backing file’s device. |
| 5 | Inode / shmid | 1577924 | For regular files: inode number. For System V shm: the shmid (integer returned by shmget()). |
| 6 | Name / Tag | SYSV00000000 (deleted) | For System V shm: “SYSV” + shmget() key in hex. “(deleted)” is normal β see explanation below. |
When a process attaches two System V shared memory segments and uses standard libraries, the maps file looks like this. Each group of lines is explained below.
/* /proc/12345/maps β annotated */
08048000-0804c000 r-xp 00000000 08:01 456789 /home/user/shm_attach /* q: text segment */
0804c000-0804d000 rw-p 00003000 08:01 456789 /home/user/shm_attach /* q: data segment */
0804d000-0804e000 r--p 00004000 08:01 456789 /home/user/shm_attach /* q: string constants (read-only) */
b7e00000-b7e10000 rw-s 00000000 00:06 3 SYSV00000000 (deleted) /* w: shm segment 1 */
b7e10000-b7e20000 rw-s 00000000 00:06 4 SYSV00000000 (deleted) /* w: shm segment 2 */
b7e20000-b7f60000 r-xp 00000000 08:01 1234 /lib/libc-2.17.so /* e: C library text */
b7f60000-b7f62000 rw-p 00140000 08:01 1234 /lib/libc-2.17.so /* e: C library data */
b7f70000-b7f90000 r-xp 00000000 08:01 5678 /lib/ld-2.17.so /* r: dynamic linker */
b7f90000-b7f91000 rw-p 0001f000 08:01 5678 /lib/ld-2.17.so
bffdf000-c0000000 rw-p 00000000 00:00 0 [stack] /* t: process stack */
ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso] /* y: virtual DSO */
q β Main program (3 lines)
Text segment (r-xp): executable code.
Data segment (rw-p): global/static variables.
Read-only page (r–p): string literals baked into the binary.
w β System V Shared Memory (2 lines)
Permissions show rw-s β the ‘s’ flag means shared mapping.
Column 5 = shmid (3 and 4 here).
Tag: SYSV + key in hex + “(deleted)”.
e & r β Shared Libraries
libc (standard C library) mapped in.
ld (dynamic linker) mapped in.
Private (‘p’) mappings β copy-on-write on write.
t & y β Stack and vDSO
[stack]: grows downward toward lower addresses.
[vdso]: virtual dynamic shared object. Added since kernel 2.6.12. Used for fast user-space syscalls.
The permission field is always 4 characters, e.g. rw-s or r-xp.
The tag for a System V shared memory segment has two parts:
SYSV00000000
“SYSV” is a fixed prefix the kernel always adds.
The hex number after it is the key that was passed to shmget(). When you use IPC_PRIVATE (value = 0), the hex digits are all zeros. If you used a key like 0xABCD1234, the tag would show SYSVabcd1234.
(deleted)
This is not an error and does not mean the segment is broken.
The kernel internally backs System V shared memory with a file in a hidden tmpfs filesystem (RAM-based). It immediately unlinks (deletes) the directory entry after creating it β exactly like the pattern of opening a file and calling unlink() before using it. The data still lives in RAM; only the name is gone. Hence “(deleted)”.
/* How the kernel implements System V shm internally (simplified concept) */
/* Step 1: create a file in a hidden tmpfs (RAM filesystem) */
int fd = open("/dev/shm/.internal_shm_XXXX", O_RDWR | O_CREAT, 0600);
/* Step 2: immediately unlink β removes the name, data stays in RAM */
unlink("/dev/shm/.internal_shm_XXXX");
/* Step 3: shmat() mmap()-maps this anonymous fd into the process VA space */
void *addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
/* Result in /proc/PID/maps:
b7e40000-b7e41000 rw-s 00000000 00:06 3 SYSV00000000 (deleted)
The "(deleted)" is because the tmpfs name was unlinked in step 2. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/shm.h>
#define SHM_SIZE 4096
int main(void)
{
int shmid;
char *addr;
FILE *fp;
char line[512];
/* Create a shared memory segment using IPC_PRIVATE */
shmid = shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0660);
if (shmid == -1) { perror("shmget"); exit(EXIT_FAILURE); }
/* Attach β kernel chooses the virtual address */
addr = shmat(shmid, NULL, 0);
if (addr == (void *)-1) { perror("shmat"); exit(EXIT_FAILURE); }
printf("Shared memory attached at: %p (shmid=%d)\n\n", addr, shmid);
/* Open /proc/self/maps β this is our own process's map */
fp = fopen("/proc/self/maps", "r");
if (!fp) { perror("fopen"); exit(EXIT_FAILURE); }
printf("=== Lines containing 'SYSV' in /proc/self/maps ===\n");
while (fgets(line, sizeof(line), fp) != NULL) {
if (strstr(line, "SYSV"))
printf(" %s", line);
}
fclose(fp);
shmctl(shmid, IPC_RMID, NULL);
shmdt(addr);
return 0;
}
/*
* Compile: gcc -o maps_demo maps_demo.c
* Run: ./maps_demo
*
* Sample output:
* Shared memory attached at: 0xb7e40000 (shmid=3)
*
* === Lines containing 'SYSV' in /proc/self/maps ===
* b7e40000-b7e41000 rw-s 00000000 00:06 3 SYSV00000000 (deleted)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/shm.h>
int main(void)
{
int id1, id2;
char *a1, *a2;
FILE *fp;
char line[512];
id1 = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0660);
id2 = shmget(IPC_PRIVATE, 8192, IPC_CREAT | 0660);
a1 = shmat(id1, NULL, 0);
a2 = shmat(id2, NULL, 0);
printf("Segment 1: shmid=%-4d addr=%p size=4096\n", id1, a1);
printf("Segment 2: shmid=%-4d addr=%p size=8192\n\n", id2, a2);
fp = fopen("/proc/self/maps", "r");
printf("%-40s %-6s Type\n", "Address Range", "Perms");
printf("%-40s %-6s ----\n", "-------------", "-----");
while (fgets(line, sizeof(line), fp) != NULL) {
char range[40], perms[8], off[12], dev[8], ino[16], name[256];
name[0] = '\0';
sscanf(line, "%39s %7s %11s %7s %15s %255[^\n]",
range, perms, off, dev, ino, name);
const char *type =
strstr(name, "SYSV") ? "*** SHM SEGMENT ***" :
strstr(name, ".so") ? "shared library" :
strstr(name, "[stack]") ? "stack" :
strstr(name, "[vdso]") ? "vdso" :
strlen(name) > 0 ? "file mapping" :
"anonymous";
printf("%-40s %-6s %s\n", range, perms, type);
}
fclose(fp);
shmctl(id1, IPC_RMID, NULL);
shmctl(id2, IPC_RMID, NULL);
shmdt(a1);
shmdt(a2);
return 0;
}
[vdso] stands for Virtual Dynamic Shared Object. The kernel maps a tiny shared library into every process automatically. Its purpose is to make certain very-frequent system calls faster by running them partly in user space, avoiding the full user-to-kernel context switch overhead.
Why it exists
Calls like gettimeofday() and clock_gettime() are called millions of times per second in some programs. A normal syscall requires a ring-3 to ring-0 transition. The vDSO lets these calls read kernel-maintained data (like the current time) directly from user space β no context switch needed.
When it appears
The [vdso] entry appears only in kernels since version 2.6.12. On older kernels a different mechanism called [vsyscall] was used at a fixed address. The vDSO is mapped at a random address each time (ASLR-aware).
Q1: What is /proc/PID/maps and why is it useful for shared memory debugging?
Answer: It is a virtual file exposed by the Linux kernel that shows the complete virtual address space layout of process PID β every segment on one line each. For shared memory debugging it lets you confirm that a segment was attached, see what address it landed at, verify permissions, read the shmid from column 5, and check whether it has been marked for deletion.
Q2: How do you distinguish a System V shared memory segment from a regular file mapping in /proc/PID/maps?
Answer: By column 6 (the name/tag). A System V shm segment shows SYSV followed by the key in hex, e.g. SYSV00000000 (deleted). A regular file mapping shows a full filesystem path. Additionally, shm is always flagged s (shared) in the permissions; regular private file maps use p.
Q3: Why does a System V shared memory segment appear as “(deleted)” even though it is in use?
Answer: The kernel implements System V shm by creating a file in a hidden tmpfs RAM filesystem, then immediately unlinking its directory entry. The data stays in RAM as long as any process maps it β only the name is gone. This is the same open-then-unlink pattern used to create anonymous temporary files. The “(deleted)” label is a normal implementation artifact, not an error.
Q4: A segment is created with IPC_PRIVATE key. What tag appears in /proc/PID/maps?
Answer: SYSV00000000 (deleted). The zeros appear because IPC_PRIVATE has the numeric value 0. The format is always “SYSV” + key in hex. If you had used key 0xDEADBEEF, you would see SYSVdeadbeef (deleted).
Q5: What is the difference between the ‘p’ and ‘s’ flags in the permissions column?
Answer: p = private (copy-on-write). The process gets its own copy of the page if it writes β changes not visible to others. s = shared. All processes sharing this mapping see each other’s writes in real time. System V shared memory is always s β sharing data is the entire purpose.
Q6: What does column 5 represent for a System V shared memory segment?
Answer: For normal file mappings column 5 is the inode number of the file. For System V shm it holds the shmid β the integer identifier returned by shmget(). You can use this to cross-reference with ipcs -m which lists all segments by shmid.
Q7: Two processes attach the same segment. Will it appear at the same address in both /proc/PID/maps files?
Answer: Not necessarily. When shmat(shmid, NULL, 0) is called β NULL means “let the kernel choose” β the kernel picks a free region independently for each process. Both will show the same shmid in column 5 and the same SYSV tag, but the address range in column 1 may differ. This is exactly why storing absolute pointers inside shared memory is wrong β you must use relative offsets instead.
Learn the correct offset-based technique for building linked lists and trees inside shared memory.
β Storing Pointers in Shared Memory EmbeddedPathashala Home
