mmap() maps a file (or shared memory object) into the calling process’s virtual address space. With MAP_SHARED, all processes that map the same object see each other’s writes immediately β because they all share the same underlying physical memory pages.
This is the fastest form of IPC: writing to shared memory is as fast as writing to a normal variable. No system calls are involved after the initial mmap().
#include <sys/mman.h>
void *mmap(void *addr, /* Suggested address (usually NULL) */
size_t length, /* Number of bytes to map */
int prot, /* Protection flags: PROT_READ etc */
int flags, /* MAP_SHARED or MAP_PRIVATE etc */
int fd, /* File descriptor from shm_open() */
off_t offset); /* Offset into object (usually 0) */
/* Returns: mapped address on success, MAP_FAILED on error */
| Argument | Typical Value for POSIX shm | Notes |
|---|---|---|
addr |
NULL |
Let the kernel choose a suitable address |
length |
Same as ftruncate() size |
How many bytes to map from the object |
prot |
PROT_READ | PROT_WRITE |
Must match how you opened with shm_open() |
flags |
MAP_SHARED |
Writes are visible to all processes sharing the object |
fd |
From shm_open() |
Can close this fd after mmap() returns |
offset |
0 |
Map from the beginning of the object |
The prot argument controls what operations are allowed on the mapped region. The protection you request must be compatible with the access mode used in shm_open().
O_RDONLY, you cannot mmap with PROT_WRITE. The kernel will return EACCES. The write permission in prot must not exceed what was granted at shm_open() time.- Writes are immediately visible to all other processes sharing the same object
- All processes see the same physical pages
- Changes persist in the shared memory object
- This is what you want for inter-process communication
- Creates a copy-on-write mapping private to this process
- Writes are NOT visible to other processes
- Changes do not propagate back to the underlying object
- Useful for loading read-only data (like code) β not for IPC
When two processes call mmap() with MAP_SHARED on the same object, the kernel arranges their page tables to point at the same physical pages:
When you are done with the shared memory, call munmap() to release the mapping from the process’s virtual address space.
#include <sys/mman.h>
int munmap(void *addr, size_t length);
/* Returns: 0 on success, -1 on error */
- Removes the mapping from the virtual address space
- Flushes any pending writes to the shared object
- Does NOT delete the shared memory object (use
shm_unlink()for that)
addrmust be the exact address returned bymmap()lengthmust match what you passed tommap()- Mappings are also automatically removed when the process exits
A common real-world pattern is to cast the mapped address to a pointer to a struct that defines the shared data layout. Both processes use the same struct definition.
/*
* shared_struct.h β Shared data layout used by writer and reader
* Both processes include this header.
*/
#ifndef SHARED_STRUCT_H
#define SHARED_STRUCT_H
#include <stdint.h>
#define SHM_NAME "/ep_struct_demo"
#define SHM_SIZE sizeof(struct SharedData)
/* Data layout in shared memory */
struct SharedData {
int32_t counter; /* Incremented by writer */
int32_t ready; /* 1 = new data available */
char message[128]; /* Text message from writer */
};
#endif
/*
* shm_writer.c β Writes to shared memory via mmap()
* Compile: gcc shm_writer.c -o shm_writer -lrt
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include "shared_struct.h"
int main(void)
{
int fd;
struct SharedData *shdata;
/* Create shared memory object */
fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0660);
if (fd == -1) { perror("shm_open"); exit(EXIT_FAILURE); }
if (ftruncate(fd, SHM_SIZE) == -1) { perror("ftruncate"); exit(EXIT_FAILURE); }
/* Map and cast to struct pointer */
shdata = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shdata == MAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); }
close(fd); /* fd not needed after mmap */
/* Initialize */
shdata->counter = 0;
shdata->ready = 0;
memset(shdata->message, 0, sizeof(shdata->message));
printf("[Writer] Shared memory ready. Writing 5 messages...\n");
for (int i = 1; i <= 5; i++) {
shdata->counter = i;
snprintf(shdata->message, sizeof(shdata->message),
"Message #%d from writer (pid=%d)", i, (int)getpid());
shdata->ready = 1; /* Signal reader */
printf("[Writer] counter=%d message='%s'\n",
shdata->counter, shdata->message);
sleep(1);
shdata->ready = 0; /* Reset */
}
printf("[Writer] Done. Press Enter to cleanup and exit...\n");
getchar();
munmap(shdata, SHM_SIZE);
shm_unlink(SHM_NAME);
return 0;
}
/*
* shm_reader.c β Reads from shared memory via mmap()
* Compile: gcc shm_reader.c -o shm_reader -lrt
* Run in a second terminal while writer is running.
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include "shared_struct.h"
int main(void)
{
int fd;
struct SharedData *shdata;
/* Open existing object β reader doesn't create */
fd = shm_open(SHM_NAME, O_RDONLY, 0);
if (fd == -1) { perror("shm_open"); exit(EXIT_FAILURE); }
/* Map read-only */
shdata = mmap(NULL, SHM_SIZE, PROT_READ, MAP_SHARED, fd, 0);
if (shdata == MAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); }
close(fd);
printf("[Reader] Watching for messages (Ctrl+C to stop)...\n");
int last_counter = 0;
while (1) {
/* Spin-wait for new data (simple polling β in production use semaphore) */
if (shdata->ready == 1 && shdata->counter != last_counter) {
printf("[Reader] counter=%d message='%s'\n",
shdata->counter, shdata->message);
last_counter = shdata->counter;
}
usleep(10000); /* 10ms poll interval */
}
munmap(shdata, SHM_SIZE);
return 0;
}
PTHREAD_PROCESS_SHARED) for proper synchronization without busy-waiting.MAP_SHARED causes writes by one process to be immediately visible to all other processes mapping the same object, because they all share the same physical memory pages. MAP_PRIVATE creates a copy-on-write mapping β writes stay private to the process and never propagate to the object or other processes.ftruncate() or fstat() later.mmap() returns the special constant MAP_FAILED (which is (void *) -1) on error. You must check addr == MAP_FAILED, not addr == NULL. A return of NULL would be a valid mapping at address zero in some architectures, which is why MAP_FAILED is a distinct sentinel.mmap() returns MAP_FAILED and sets errno to EACCES (permission denied). The write protection in prot cannot exceed the access rights granted when the object was opened with shm_open().mmap() and the same length that was passed to mmap(). If you try to unmap a partial range or a wrong address, munmap() will fail with EINVAL.munmap() only removes the mapping from the calling process’s virtual address space. The shared memory object in /dev/shm continues to exist and can be opened by other processes. To delete the object, you must call shm_unlink().β Part 3: ftruncate() & fstat() Part 5: shm_unlink() & Examples β
