Mapping Files Directly into Your Address Space
mmap() is a system call that bridges the gap between file I/O and memory access. Instead of reading a file with read() into a buffer, you ask the kernel to map the file's contents directly into your process's virtual address space. From that point on, the file looks like an array in memory — you access it with pointer arithmetic, not file I/O calls.
// Conceptual C example (not a full program)
int fd = open("data.bin", O_RDWR);
// Map the first 4096 bytes of the file into memory
char *data = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// Now just use it like an array — no read() or write() needed
data[0] = 'X'; // This modifies the file on disk (eventually)
When you read from the mapped region, the kernel checks if the corresponding file page is already in the page cache (RAM). If yes, it returns instantly. If no, it triggers a page fault, loads the page from disk into the page cache, and then returns. When you write to the mapped region, the kernel marks the page as dirty and eventually flushes it back to disk (this is called writeback). You can force an immediate flush with msync().
Why databases love mmap:
Traditional file I/O requires two copies: disk → kernel page cache → application buffer. With mmap, the application accesses the kernel page cache directly — one copy eliminated. The OS virtual memory manager also handles caching automatically: frequently accessed pages stay in RAM, cold pages get evicted. The database gets a sophisticated LRU cache for free without writing any caching code.
- LMDB (Lightning Memory-Mapped Database): Used by OpenLDAP and many embedded systems. The entire database is a single
mmap'd file. Reads are pure memory accesses with zero system calls. - SQLite WAL mode: Uses
mmapfor the main database file. - MongoDB MMAPv1 (legacy): Mapped the entire data file. Replaced by WiredTiger, which manages its own cache, giving more control over memory pressure.
The mmap trade-off:
mmap is not universally better. Its weakness is unpredictable latency. A page fault can stall your process for milliseconds while the OS fetches data from disk. In a latency-sensitive system, this is unacceptable. Databases like PostgreSQL deliberately avoid mmap for their main data files and instead use read()/write() with their own buffer pool, because they need precise control over when I/O happens and can handle errors explicitly. With mmap, a disk I/O error surfaces as a SIGBUS signal — much harder to handle gracefully than a read() returning -1.
A common interview question: "Why does PostgreSQL not use mmap for its data files, even though mmap is faster?" The answer has three parts: (1) PostgreSQL needs to handle I/O errors gracefully — mmap surfaces disk errors as signals (SIGBUS), not return codes; (2) PostgreSQL needs precise control over dirty page writeback for crash consistency (WAL ordering); (3) the OS page eviction policy (LRU) is not optimal for database access patterns — PostgreSQL's own buffer pool uses a clock-sweep algorithm tuned for database workloads.