What is MAP_FIXED?
Normally when you call mmap(), you pass an address hint in the addr parameter. The kernel treats this as a suggestion and is free to place the mapping anywhere it likes. The actual address is returned by mmap().
When you specify MAP_FIXED, the kernel must place the mapping at exactly the address specified in addr. That address must be page-aligned. If any existing mapping occupies any part of the requested range, those pages are silently unmapped first.
MAP_FIXED is generally non-portable (different systems handle it differently) and dangerous if misused. However, there is one important portable use case: mapping multiple file regions into a contiguous address range.
If you blindly specify MAP_FIXED with an arbitrary address, you may overwrite critical regions of your process:
- Your stack
- Existing mmap() mappings (shared libraries, etc.)
- The heap
- Program text/data segments
Linux silently unmaps whatever was there before and places your new mapping at that address. There is no error. This causes hard-to-debug crashes.
/* DANGEROUS: overlaps unknown regions */
void *p = mmap((void *)0x400000, 4096,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
-1, 0);
/* This may have just unmapped part of your .text segment! */
Safe practice: If you need MAP_FIXED, always first create a large anonymous reservation to let the kernel choose a safe region, then overlay file mappings on top of that reservation.
This is the one scenario where a portable application should use MAP_FIXED. The goal: map several non-contiguous regions of a file (or multiple files) so they appear as a single contiguous block in memory.
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
int main(void)
{
size_t pgsz = getpagesize();
/* Step 1: Create initial anonymous mapping โ let kernel choose address */
char *base = mmap(NULL, 2 * pgsz,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
if (base == MAP_FAILED) { perror("mmap base"); return 1; }
memset(base, 'A', 2 * pgsz);
printf("Base mapping at %p, first byte = %c\n", base, base[0]);
/* Step 2: Overlay the second page with a new anonymous mapping */
char *overlay = mmap(base + pgsz, pgsz,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
-1, 0);
if (overlay == MAP_FAILED) { perror("mmap overlay"); return 1; }
/* overlay == base + pgsz guaranteed */
memset(overlay, 'B', pgsz);
printf("First page: %c (original)\n", base[0]);
printf("Second page: %c (overlaid with MAP_FIXED)\n", base[pgsz]);
munmap(base, 2 * pgsz);
return 0;
}
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
/*
* Create a test file and map two separate regions of it
* into a contiguous virtual address range.
*/
int main(void)
{
size_t pgsz = getpagesize();
const char *fname = "/tmp/test_mapfixed.bin";
/* Create a 4-page test file */
int fd = open(fname, O_CREAT | O_RDWR | O_TRUNC, 0600);
if (fd == -1) { perror("open"); return 1; }
ftruncate(fd, 4 * pgsz);
/* Write known data to each page */
char page_data[4096];
for (int p = 0; p < 4; p++) {
memset(page_data, 'A' + p, pgsz);
pwrite(fd, page_data, pgsz, p * pgsz);
}
/*
* Goal: Map page 0 and page 3 of the file into
* contiguous virtual addresses [base, base+2*pgsz)
*/
/* Step 1: Reserve 2 pages of VA space anonymously โ safe address */
char *base = mmap(NULL, 2 * pgsz,
PROT_NONE, /* no access yet */
MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
if (base == MAP_FAILED) { perror("mmap reserve"); close(fd); return 1; }
printf("Reserved VA range: %p - %p\n", base, base + 2 * pgsz);
/* Step 2: Overlay first slot with file page 0 */
char *slot0 = mmap(base, pgsz,
PROT_READ,
MAP_SHARED | MAP_FIXED,
fd, 0); /* offset = 0 (page 0) */
if (slot0 == MAP_FAILED) { perror("mmap slot0"); return 1; }
/* Step 3: Overlay second slot with file page 3 */
char *slot1 = mmap(base + pgsz, pgsz,
PROT_READ,
MAP_SHARED | MAP_FIXED,
fd, 3 * pgsz); /* offset = page 3 */
if (slot1 == MAP_FAILED) { perror("mmap slot1"); return 1; }
printf("base[0] = %c (from file page 0)\n", base[0]);
printf("base[pgsz] = %c (from file page 3)\n", base[pgsz]);
/* The two non-contiguous file pages now appear contiguous in VA */
printf("Pointer arithmetic: base[pgsz - 1]=%c, base[pgsz]=%c\n",
base[pgsz - 1], base[pgsz]);
munmap(base, 2 * pgsz);
close(fd);
unlink(fname);
return 0;
}
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#define N_FILES 3
int main(void)
{
size_t pgsz = getpagesize();
const char *filenames[N_FILES] = {
"/tmp/mapfile_a.bin",
"/tmp/mapfile_b.bin",
"/tmp/mapfile_c.bin"
};
int fds[N_FILES];
size_t sizes[N_FILES] = { pgsz, pgsz, pgsz };
size_t total = 0;
/* Create 3 test files, each 1 page, filled with distinct characters */
for (int i = 0; i < N_FILES; i++) {
fds[i] = open(filenames[i], O_CREAT | O_RDWR | O_TRUNC, 0600);
if (fds[i] == -1) { perror("open"); return 1; }
ftruncate(fds[i], pgsz);
char buf[4096];
memset(buf, 'X' + i, pgsz);
write(fds[i], buf, pgsz);
total += sizes[i];
}
/* Step 1: Reserve contiguous VA space for all files */
char *base = mmap(NULL, total,
PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
if (base == MAP_FAILED) { perror("reserve"); return 1; }
/* Step 2: Overlay each file into its slot */
size_t offset = 0;
for (int i = 0; i < N_FILES; i++) {
void *addr = mmap(base + offset, sizes[i],
PROT_READ,
MAP_SHARED | MAP_FIXED,
fds[i], 0);
if (addr == MAP_FAILED) { perror("overlay"); return 1; }
offset += sizes[i];
close(fds[i]);
}
/* All three files now appear as one contiguous buffer */
for (int i = 0; i < N_FILES; i++) {
printf("File %d at base[%zu] = %c\n",
i, i * pgsz, base[i * pgsz]);
}
munmap(base, total);
for (int i = 0; i < N_FILES; i++) unlink(filenames[i]);
return 0;
}
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(void)
{
size_t pgsz = getpagesize();
printf("Page size: %zu bytes\n", pgsz);
/* Reserve a region first */
char *base = mmap(NULL, 4 * pgsz,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
if (base == MAP_FAILED) { perror("mmap"); return 1; }
/* Try MAP_FIXED with a non-aligned address โ must fail */
void *bad_addr = base + 100; /* not page-aligned */
void *p = mmap(bad_addr, pgsz,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
-1, 0);
if (p == MAP_FAILED) {
printf("MAP_FIXED with unaligned addr failed as expected: %s\n",
strerror(errno));
} else {
printf("Unexpectedly succeeded at %p\n", p);
}
/* Try with aligned address โ should succeed */
void *good_addr = base + pgsz; /* page-aligned */
p = mmap(good_addr, pgsz,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
-1, 0);
if (p == MAP_FAILED) {
printf("MAP_FIXED with aligned addr failed: %s\n", strerror(errno));
} else {
printf("MAP_FIXED with aligned addr succeeded at %p\n", p);
}
munmap(base, 4 * pgsz);
return 0;
}
| Property | Without MAP_FIXED | With MAP_FIXED |
|---|---|---|
| Address placement | Kernel chooses (hint used) | Exactly addr (mandatory) |
| Alignment requirement | None (hint can be 0) | Must be page-aligned |
| Existing mappings | Not affected | Silently unmapped |
| Portability | Fully portable (POSIX) | Non-portable (varies) |
| Safety | Safe | Risky if used carelessly |
| Portable use case | General use | Contiguous multi-region file mapping |
Q1. What does MAP_FIXED do in mmap()?
A: It forces the kernel to place the mapping at exactly the address specified in addr. Without MAP_FIXED, addr is just a hint and the kernel may choose a different address. With MAP_FIXED, the returned address will always equal addr. Any existing mapping at that VA range is silently unmapped first.
Q2. Why is MAP_FIXED dangerous?
A: If you specify an address that already holds a valid mapping (stack, heap, shared library, program text), that mapping is silently destroyed. There is no error or warning. Your process can immediately crash or behave unpredictably. The kernel does not protect you from this.
Q3. What is the safe way to use MAP_FIXED portably?
A: First call mmap() without MAP_FIXED to reserve a large anonymous region (letting the kernel choose a safe address). Then use MAP_FIXED to overlay file regions at specific offsets within that reserved region. This guarantees no pre-existing mapping is accidentally destroyed.
Q4. What happens to the bytes in an overlaid MAP_FIXED region?
A: The original anonymous pages are replaced by the new mapping. Any data in the original anonymous mapping at that range is lost. The new mapping’s contents are what you specified (file data, new anonymous zeroed pages, etc.).
Q5. Why would you map multiple file regions into a contiguous VA range?
A: To allow pointer arithmetic across regions without knowing boundaries. For example, combining a file header (first 4KB) with a data section (at a later offset) so you can walk through them as a single buffer. This simplifies code that processes non-contiguous file sections as if they were adjacent in memory.
Q6. Does MAP_FIXED require addr to be page-aligned?
A: Yes. MAP_FIXED requires addr to be a multiple of the system page size (getpagesize()). An unaligned address causes mmap() to fail with EINVAL.
Q7. What is the relationship between MAP_FIXED and MREMAP_FIXED?
A: They serve analogous purposes for different syscalls. MAP_FIXED forces mmap() to place a new mapping at a specific address. MREMAP_FIXED forces mremap() to move an existing mapping to a specific address. Both require the address to be page-aligned, and both silently unmap whatever was there.
Q8. Can MAP_FIXED overlap with the program’s stack or text segment?
A: Technically yes โ the kernel will unmap those regions and place your mapping there. In practice this immediately crashes the process. This is why you should never use MAP_FIXED with an arbitrary address unless you have reserved that VA range safely first.
| Topic | Key Syscall / File | Core Idea |
|---|---|---|
| mremap() | mremap() |
Resize/relocate existing mapping without copying |
| MAP_NORESERVE | mmap(MAP_NORESERVE), /proc/sys/vm/overcommit_* |
Lazy swap reservation; overcommit control |
| OOM Killer | /proc/PID/oom_score, /proc/PID/oom_adj |
Kill processes to recover memory under OOM |
| MAP_FIXED | mmap(MAP_FIXED) |
Force mapping at exact VA; safe use for contiguous file mapping |
mremap() MAP_NORESERVE OOM Killer
