mmap Overview
Intermediate
TLPI Ch49
TI / ST / Qualcomm
A memory mapping is a region of a process’s virtual address space that is backed by either a file on disk or by anonymous (zero-filled) memory. The kernel manages this mapping so that reading/writing to that virtual address range actually reads/writes the underlying backing store โ without the process manually calling read() or write().
Memory mappings are created using the mmap() system call. They are widely used for:
- Loading program text and data segments (the OS does this automatically)
- Memory-mapped file I/O (fast alternative to read/write)
- Inter-process communication (IPC) between related or unrelated processes
- Dynamic memory allocation (glibc
malloc()uses this internally) - Sharing code between processes via shared libraries
Every mapping created by mmap() has exactly two independent attributes. Think of them as two separate on/off switches:
| Type | Backed By |
|---|---|
| File Mapping | A region of a file on disk |
| Anonymous Mapping | Zero-initialized RAM (no file) |
| Flag | Modifications |
|---|---|
| MAP_PRIVATE | Private to process (copy-on-write) |
| MAP_SHARED | Visible to all sharers + written to file |
These two dimensions combine to produce 4 types of mappings (covered in the next section).
The combination of file/anonymous ร private/shared gives four distinct mapping types, each used for a different purpose:
| Combination | Primary Use Case | Writes visible to others? | Written to file? |
|---|---|---|---|
| Private + File | Load program text/data, read-only file init | No (copy-on-write) | No |
| Private + Anonymous | Memory allocation (malloc large blocks) | No (copy-on-write) | N/A |
| Shared + File | Memory-mapped I/O, IPC between unrelated processes | Yes | Yes |
| Shared + Anonymous | IPC between related processes (parent/child) | Yes | N/A |
Two processes can end up pointing to the same physical RAM pages in two ways:
Virtual Pages โ Physical RAM page X
Virtual Pages โ Physical RAM page X
Virtual Pages โ Physical RAM page Y
Virtual Pages โ Physical RAM page Y
Whether these processes actually see each other’s writes depends on MAP_PRIVATE vs MAP_SHARED.
With MAP_PRIVATE, modifications are not visible to other processes. The kernel achieves this through the copy-on-write mechanism:
This is exactly the same mechanism used by fork() for the process’s heap and stack pages.
With MAP_SHARED, all processes that map the same region share the same physical pages. There is no copy-on-write. A write by one process is immediately visible to all others sharing that mapping.
For file-backed shared mappings, writes are also propagated back to the underlying file on disk (though potentially with a delay โ the kernel’s page cache handles this). This is the basis of memory-mapped I/O.
| Attribute | MAP_PRIVATE | MAP_SHARED |
|---|---|---|
| Writes visible to others | No | Yes |
| Copy-on-Write | Yes (kernel handles) | No |
| File changes propagated | No | Yes (for file mappings) |
| Survives exec() | No (mappings lost) | No (mappings lost) |
| Inherited by fork() | Yes | Yes |
Linux exposes every mapping of a process in /proc/PID/maps. Each line shows: address range, permissions, offset, device, inode, and pathname (if file-backed).
/* Read and print your own process's memory map */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
char path[64];
char line[256];
FILE *fp;
/* /proc/self/maps is shorthand for /proc/<our-PID>/maps */
snprintf(path, sizeof(path), "/proc/self/maps");
fp = fopen(path, "r");
if (fp == NULL) {
perror("fopen /proc/self/maps");
exit(EXIT_FAILURE);
}
printf("%-40s %-6s %-8s %s\n",
"Address Range", "Perms", "Offset", "Pathname");
printf("%s\n", "-----------------------------------------------------------");
while (fgets(line, sizeof(line), fp) != NULL) {
printf("%s", line);
}
fclose(fp);
return 0;
}
Sample output fields:
7f8a3c000000-7f8a3c021000 r--p 00000000 08:01 12345 /lib/x86_64-linux-gnu/libc.so.6
7f8a3c021000-7f8a3c176000 r-xp 00021000 08:01 12345 /lib/x86_64-linux-gnu/libc.so.6
7fff3b400000-7fff3b421000 rw-p 00000000 00:00 0 [stack]
Notice: r-xp = read + execute + private (code), rw-p = read + write + private (stack/heap), r--s would be read + shared.
read()/write() system calls for file I/O, enables efficient IPC by sharing pages between processes, and allows the OS to lazily load program segments from disk.MAP_PRIVATE gives each process its own copy-on-write view โ writes are not visible to other processes and not propagated to any backing file. MAP_SHARED means all processes sharing the mapping see each other’s writes, and for file-backed mappings, writes are also flushed to the file.malloc() allocating large memory blocks. (3) Shared + File: Memory-mapped I/O and IPC between unrelated processes via a file. (4) Shared + Anonymous: IPC between a parent and child process created by fork().fork(): Yes โ the child inherits all of the parent’s mappings (both MAP_PRIVATE and MAP_SHARED), including the mapping type. On exec(): No โ all mappings are destroyed (the process image is replaced). The new program gets its own fresh mappings created by the kernel when loading the ELF.