Once a mapping is created with mmap(), its size is fixed. If you need more (or less) space, Linux provides mremap() to grow or shrink it without creating an entirely new mapping. Think of it as realloc() for memory mappings.
#define _GNU_SOURCE
#include <sys/mman.h>
void *mremap(void *old_address, /* Start of existing mapping */
size_t old_size, /* Current size */
size_t new_size, /* Desired new size */
int flags, /* MREMAP_MAYMOVE and/or MREMAP_FIXED */
...); /* new_address (only if MREMAP_FIXED) */
/* Returns new address on success, MAP_FAILED on error */
| Flag | Meaning |
MREMAP_MAYMOVE |
Allow the kernel to move the mapping to a new address if there is not enough contiguous space to grow it in place. Without this flag, mremap() fails if it cannot grow in place. |
MREMAP_FIXED |
Move the mapping to the address specified as the 5th argument. Any existing mapping at that address is replaced. Must be combined with MREMAP_MAYMOVE. |
| Before mremap() — 3-page mapping: | |||
| Page 0 | Page 1 | Page 2 | [free space? maybe] |
| After mremap() to 5 pages with MREMAP_MAYMOVE — may relocate: | |||
| Page 0 | Page 1 | Page 2 | Page 3 (new) | Page 4 (new) ← at new address | |||
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
int main(void) {
long ps = sysconf(_SC_PAGESIZE);
/* Start with a 1-page anonymous mapping */
char *addr = mmap(NULL, ps,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
if (addr == MAP_FAILED) { perror("mmap"); exit(1); }
strcpy(addr, "Original data in page 0");
printf("Before mremap: %s\n", addr);
/* Grow to 4 pages — allow moving if needed */
char *new_addr = mremap(addr, ps, 4 * ps, MREMAP_MAYMOVE);
if (new_addr == MAP_FAILED) { perror("mremap"); exit(1); }
/* Data at original offset is preserved after move */
printf("After mremap: %s\n", new_addr);
/* New pages are accessible and zero-filled */
new_addr[ps] = 'B';
printf("New page 1 first byte: %c\n", new_addr[ps]);
munmap(new_addr, 4 * ps);
return 0;
}
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
int main(void) {
long ps = sysconf(_SC_PAGESIZE);
/* Allocate 8 pages */
char *addr = mmap(NULL, 8 * ps,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
if (addr == MAP_FAILED) { perror("mmap"); exit(1); }
printf("Allocated 8 pages at %p\n", (void*)addr);
/* Shrink to 2 pages — no flag needed for shrinking */
char *new_addr = mremap(addr, 8 * ps, 2 * ps, 0);
if (new_addr == MAP_FAILED) { perror("mremap"); exit(1); }
printf("Shrunk to 2 pages at %p\n", (void*)new_addr);
/* Pages 2–7 are now unmapped — accessing them = SIGSEGV */
munmap(new_addr, 2 * ps);
return 0;
}
Normally, you pass NULL as addr to mmap() and let the kernel choose where to place the mapping. With MAP_FIXED, the kernel maps exactly at the address you specify, silently removing any existing mapping in the way.
MAP_FIXED is dangerous. If the specified address overlaps a critical region (stack, code, libc), it silently destroys it. The safe pattern is: first reserve a large contiguous region with a normal mmap(NULL, ...), then use MAP_FIXED within that reserved region to place specific sub-mappings. This guarantees you are only overwriting your own placeholder.#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
int main(void) {
long ps = sysconf(_SC_PAGESIZE);
int fd;
char *region, *slot0, *slot1;
/* Step 1: Reserve a contiguous 4-page address range */
region = mmap(NULL, 4 * ps,
PROT_NONE, /* No access yet */
MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
if (region == MAP_FAILED) { perror("mmap reserve"); exit(1); }
printf("Reserved region at %p\n", (void*)region);
/* Step 2: Open two files to map within the reserved region */
fd = open("/etc/hostname", O_RDONLY);
if (fd == -1) { perror("open"); exit(1); }
/* Step 3: Map file into page 0 of the reserved region using MAP_FIXED */
slot0 = mmap(region, ps,
PROT_READ,
MAP_PRIVATE | MAP_FIXED, /* MAP_FIXED: exactly at region */
fd, 0);
if (slot0 == MAP_FAILED) { perror("mmap fixed slot0"); exit(1); }
close(fd);
printf("slot0 (hostname): %p → %.30s\n", (void*)slot0, slot0);
/* Step 4: Map anonymous memory into pages 2-3 */
slot1 = mmap(region + 2 * ps, 2 * ps,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
-1, 0);
if (slot1 == MAP_FAILED) { perror("mmap fixed slot1"); exit(1); }
slot1[0] = 'Z';
printf("slot1 (anon): %p → %c\n", (void*)slot1, slot1[0]);
munmap(region, 4 * ps);
return 0;
}
When you call mmap() (or malloc()), the kernel does not immediately allocate physical RAM for every page. It just reserves virtual address space. Physical pages are allocated on demand when the page is first accessed. This is called demand paging.
Because processes rarely use all their allocated virtual memory, Linux allows the total committed virtual memory to exceed the available physical RAM + swap. This is overcommitting. It works well in practice because most allocations are never fully used.
| Process A Committed: 500 MB Actually used: 80 MB |
Process B Committed: 400 MB Actually used: 60 MB |
Process C Committed: 300 MB Actually used: 50 MB |
Physical RAM + Swap: 512 MB (total committed = 1.2 GB ✓ allowed) |
If too many pages are actually accessed, physical RAM and swap fill up. The kernel’s Out-Of-Memory (OOM) killer steps in and kills one or more processes to free memory. This can happen long after mmap() succeeded, which is a surprise to programs expecting an immediate failure.
# View current overcommit policy
cat /proc/sys/vm/overcommit_memory
# 0 = heuristic overcommit (default)
# 1 = always overcommit (never fail mmap)
# 2 = never overcommit beyond commit limit
# View overcommit ratio (used when policy = 2)
cat /proc/sys/vm/overcommit_ratio
# e.g., 50 means total commit limit = 50% of RAM + swap
# Set policy (as root)
echo 2 > /proc/sys/vm/overcommit_memory
echo 80 > /proc/sys/vm/overcommit_ratio
Normally, when you create a mapping, the kernel reserves swap space as a safety net. MAP_NORESERVE skips this reservation — the mapping succeeds even if swap is nearly full. The trade-off: if you later access a page and there truly is no physical memory or swap available, the OOM killer fires or the access fails with SIGSEGV.
/* Large sparse mapping — don't reserve swap for all pages */
char *sparse = mmap(NULL, 10UL * 1024 * 1024 * 1024, /* 10 GB virtual */
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
-1, 0);
/* This can succeed even on a 4 GB RAM machine.
Pages are only backed by RAM when actually touched. */
#include <stdio.h>
#include <stdlib.h>
void print_overcommit_policy(void) {
FILE *f;
int policy;
f = fopen("/proc/sys/vm/overcommit_memory", "r");
if (!f) { perror("fopen"); return; }
fscanf(f, "%d", &policy);
fclose(f);
printf("Overcommit policy: %d — ", policy);
switch (policy) {
case 0: printf("Heuristic (default)\n"); break;
case 1: printf("Always overcommit\n"); break;
case 2: printf("Strict limit\n"); break;
default: printf("Unknown\n");
}
}
void print_commit_limit(void) {
FILE *f = fopen("/proc/meminfo", "r");
if (!f) { perror("fopen"); return; }
char line[256];
while (fgets(line, sizeof(line), f)) {
if (strncmp(line, "CommitLimit", 11) == 0 ||
strncmp(line, "Committed_AS", 12) == 0) {
printf("%s", line);
}
}
fclose(f);
}
int main(void) {
print_overcommit_policy();
print_commit_limit();
return 0;
}
A: mremap() resizes an existing mapping in place (or moves it with MREMAP_MAYMOVE). It is more efficient than unmap + remap because: (1) it preserves the page table entries for unchanged pages — no page faults needed on the retained data; (2) it avoids the window of time between unmap and remap where the address range is unmapped; (3) it is a single system call.
A: mremap() returns MAP_FAILED with errno set to ENOMEM. Without MREMAP_MAYMOVE, the kernel is not allowed to relocate the mapping, so if the virtual address range immediately after the current mapping is already occupied, the resize fails.
A: Overcommitting means the kernel allows processes to reserve (commit) more virtual memory in total than the physical RAM + swap available. The benefit is efficiency: most programs allocate large buffers but only use a fraction. By not requiring physical RAM up front, many more processes can run concurrently. The kernel assigns physical pages only when a page is first accessed (demand paging).
A: The Linux OOM (Out-Of-Memory) killer. mmap() succeeds because virtual address space is reserved (overcommit), but physical RAM + swap runs out later when many pages are accessed. The kernel kills the process (or another one) to reclaim memory. This can be mitigated by: using overcommit_memory=2 (strict mode) so mmap() fails early, or by mlockall()/mlock() to pre-fault and lock pages into RAM.
A: First, create a large anonymous mapping with MAP_PRIVATE | MAP_ANONYMOUS (no MAP_FIXED) to reserve a contiguous virtual address range. The kernel guarantees this range is yours. Then use MAP_FIXED within that range to place specific file mappings or different-protection sub-regions. This avoids accidentally overwriting critical regions because you are only replacing your own placeholder mapping.
