HOWTO · C
How to Use the mmap Function to Write to the Memory in C
Learn how mmap() maps memory in C, how to choose its arguments, and how to write to anonymous memory.
On this page
mmap() creates a memory mapping in a process. You can use a writable anonymous mapping as memory for C data, or use a shared anonymous mapping to pass changes between related processes. This guide explains the six arguments, checks mapping failures, and shows how to release a mapping with munmap().
What mmap() Does and Returns
A mapping connects a range of a process’s virtual address space to a file, a device, or anonymous memory. Anonymous memory is not backed by a file, so changes to it do not persist to a file. Include <sys/mman.h> to use mmap() and its related constants.
The function takes six arguments and returns the address of the new mapping on success. On failure it returns MAP_FAILED, so compare against that sentinel rather than checking for NULL or zero. The call can also set errno, which can help diagnose a failure.
Understand the mmap() Parameters and Flags
The arguments, in order, are address, length, prot, flags, fd, and offset:
addressis a suggested starting address. PassingNULLlets the kernel choose a suitable address; applications usually do this.lengthis the number of bytes to map. The kernel handles page alignment for the mapped range, but use the requested length consistently when releasing it.protsets access permissions. CombinePROT_READandPROT_WRITEfor data you need to read and modify. AddPROT_EXEConly when executable memory is required.flagsselects sharing and backing behavior.MAP_PRIVATEmakes updates private to the process;MAP_SHAREDmakes updates visible through the shared mapping.MAP_ANONYMOUScreates memory without file backing. These are commonly combined with one of the sharing flags.fdidentifies a file for a file-backed mapping. ForMAP_ANONYMOUS, the descriptor is ignored on common Unix-like systems; examples often pass-1.offsetselects the starting byte in a file and must be a multiple of the system page size for file-backed mappings. Use zero for an anonymous mapping.
Avoid MAP_FIXED unless you have a specific, carefully controlled address-space need: it can replace existing mappings at the requested range. These flags and fork() are provided by many Unix-like systems, but are not available in every C environment.
Write to Anonymous Memory with mmap()
This complete example maps space for five integers, writes and reads the values, then releases exactly the requested range. It assumes a Unix-like system that provides MAP_ANONYMOUS. On a successful run, it prints the output shown below.
#include <stdio.h>
#include <sys/mman.h>
int main(void) {
const size_t count = 5;
const size_t length = count * sizeof(int);
int *values = mmap(NULL, length, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (values == MAP_FAILED) {
perror("mmap");
return 1;
}
for (size_t i = 0; i < count; ++i) {
values[i] = (int)(i * 10);
}
for (size_t i = 0; i < count; ++i) {
printf("[%d] ", values[i]);
}
putchar('\n');
if (munmap(values, length) == -1) {
perror("munmap");
return 1;
}
return 0;
}
[0] [10] [20] [30] [40]
The mapping is writable because its protection combines PROT_READ and PROT_WRITE. MAP_PRIVATE is appropriate here because no other process needs to see the writes. munmap() must receive the mapped address and the same requested length; it does not take the number of elements.
Share an Anonymous Mapping Between Processes
A mapping created with MAP_SHARED can carry changes across fork(). The child and parent then access the same mapped region. The example flushes the initial line before fork() so it is not duplicated when output is redirected. The parent waits for the child before reading its updates. Like the prior example, this assumes Unix-like support for MAP_ANONYMOUS and fork(). The supplied run produced the output shown below.
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void) {
const size_t count = 5;
const size_t length = count * sizeof(int);
int *values = mmap(NULL, length, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (values == MAP_FAILED) {
perror("mmap");
return 1;
}
for (size_t i = 0; i < count; ++i) {
values[i] = (int)(i + 7);
}
printf("Initial values:");
for (size_t i = 0; i < count; ++i) {
printf(" %d", values[i]);
}
putchar('\n');
fflush(stdout);
pid_t child_pid = fork();
if (child_pid == -1) {
perror("fork");
munmap(values, length);
return 1;
}
if (child_pid == 0) {
for (size_t i = 0; i < count; ++i) {
values[i] *= 5;
}
if (munmap(values, length) == -1) {
_exit(1);
}
_exit(0);
}
int status;
if (waitpid(child_pid, &status, 0) == -1) {
perror("waitpid");
munmap(values, length);
return 1;
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
fputs("child process failed\n", stderr);
munmap(values, length);
return 1;
}
printf("Updated values:");
for (size_t i = 0; i < count; ++i) {
printf(" %d", values[i]);
}
putchar('\n');
if (munmap(values, length) == -1) {
perror("munmap");
return 1;
}
return 0;
}
Initial values: 7 8 9 10 11
Updated values: 35 40 45 50 55
The child writes through the shared mapping and exits; after waitpid() completes, the parent reads those same changes. A MAP_PRIVATE mapping would not provide this sharing behavior. Both processes release their own mapping before exiting. For file-backed mappings, MAP_SHARED can expose updates through the mapping, but persistence to the file has additional synchronization and filesystem considerations.