Core Advantages of Memory Mapped I/O
- Higher performance than standard read/write operations by reducing redundant data copies between kernel space and user space
- Intuitive file access via regular memory pointer operations, eliminating the need for custom buffer management logic
- Native support for inter-process data sharing when multiple processes map the same underlying file
- Efficient handling of large files, as data is loaded into physical memory only when accessed, rather than loading the entire file upfront
Standard Implementation Workflow
- Open the target file with appropriate read/write permissions using the
open() system call
- Retrieve the current file size by moving the file offset to the end of the file with
lseek()
- Resize the file to a 4KB aligned size (4096 bytes in this example) using
ftruncate() to ensure it can hold the mapped data
- Map the file to the process virtual address space with
mmap(), specifying access permissions and sharing behavoir
- Close the file descriptor once the mapping is created, as it is no longer required for accessing the mapped content
- Write or read data directly to/from the mapped memory region using standard memory operations
- Synchronize in-memory changes to the underlying file on disk with
msync() to prevent data loss
- Verify the written content by reading directly from the mapped memory address
- Release the mapped memory region with
munmap() when operations are complete to free system resources
Sample Implementation Code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
// Open target file with read/write permissions, create if it does not exist
int file_handle = open("mapped_io_test.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (file_handle == -1) {
perror("File open failed");
exit(EXIT_FAILURE);
}
// Calculate current file size
off_t current_file_sz = lseek(file_handle, 0, SEEK_END);
const size_t map_region_sz = 4096;
// Resize file to match mapping region size
if (ftruncate(file_handle, map_region_sz) == -1) {
perror("File resize failed");
close(file_handle);
exit(EXIT_FAILURE);
}
// Create shared read/write mapping of the file
void *map_base = mmap(NULL, map_region_sz, PROT_READ | PROT_WRITE, MAP_SHARED, file_handle, 0);
if (map_base == MAP_FAILED) {
perror("Memory mapping creation failed");
close(file_handle);
exit(EXIT_FAILURE);
}
// File descriptor can be closed after successful mapping
close(file_handle);
// Write test data to mapped memory region
const char *test_content = "Greetings from Memory Mapped I/O!";
strncpy((char *)map_base, test_content, strlen(test_content) + 1);
// Synchronize changes to persistent storage
if (msync(map_base, map_region_sz, MS_SYNC) == -1) {
perror("Data sync to disk failed");
munmap(map_base, map_region_sz);
exit(EXIT_FAILURE);
}
// Print content stored in mapped region
printf("Mapped file content: %s\n", (char *)map_base);
// Clean up mapped memory region
if (munmap(map_base, map_region_sz) == -1) {
perror("Memory mapping release failed");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
Expected Output
Mapped file content: Greetings from Memory Mapped I/O!