class: center, middle # Memory Mapped I/O ## Alan Cox and Scott Rixner --- layout: true --- # Memory Mapped I/O - Map a file or device into the address space of the process - Memory is backed by the file/device - Access the file/device as if it were memory - Allocate anonymous memory that is not backed by a file - Allows more flexibility than using `sbrk(2)` - Useful for shared memory between processes - Memory is loaded “lazily” - Only the pages that are accessed are loaded into memory --- # `mmap(2)` ``` void *mmap(void *addr[.length], size_t length, int prot, int flags, int fd, off_t offset); ``` - `addr`: Address at which to map the memory - `length`: Length (in bytes) of the mapping - `prot`: Protection flags (`PROT_READ`, `PROT_WRITE`, `PROT_EXEC`) - `flags`: Mapping flags (`MAP_SHARED`, `MAP_PRIVATE`, `MAP_ANONYMOUS`) - `fd`: File descriptor of the file to map - `offset`: Offset into the file to start mapping - Returns the address of the mapped memory on success, `MAP_FAILED` on failure --- # `mmap(2)` Illustration  --- # `munmap(2)` ``` int munmap(void *addr[.length], size_t length); ``` - `addr`: Address of the memory to unmap - `length`: Length (in bytes) of the mapping - Returns 0 on success, -1 on failure - Future accesses to region will result in a segmentation fault --- # Some Use Cases of `mmap(2)` - Handle very large files by allowing the operating system to manage the memory - Perform random/sparse accesses into a file without `read(2)`, `write(2)` or `lseek(2)` - Share memory between processes - Efficient copying of data between files/devices - Implement copy-on-write semantics --- # Sparse File Access ``` // Check for errors fd = open("sparse_file.dat", O_RDWR); status = fstat(fd, &file_info) mapped_mem = mmap(NULL, file_info.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (mapped_mem == MAP_FAILED) { perror("Failed to mmap file"); close(fd); return 1; } // Perform sparse accesses to the file (non-contiguous reads/writes) for (addr = mapped_mem; addr < mapped_mem + file_info.st_size; addr += 20000) { *addr = *addr + 10 } if (munmap(mapped_mem, file_info.st_size) == -1) { perror("Failed to munmap"); close(fd); return 1; } close(fd); ``` --- # Copy-On-Write (COW) ``` // Create an anonymous memory region using mmap (check for errors) char *mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); // Initialize memory region strcpy(mem, "Hello, World!"); printf("Before fork: %s\n", mem); pid_t pid = fork(); if (pid == 0) { strcpy(mem, "Child modified this text."); // Will print "Child modified this text." printf("Child process modified memory: %s\n", mem); exit(0); } else if (pid > 0) { wait(NULL); // Will print "Hello, World!" printf("Parent process sees memory after child modification: %s\n", mem); munmap(mem, size); } else { // Handle fork error } ``` --- # Exercises 1. Access a file using `mmap(2)` 2. Modify file contents using copy-on-write leaving the file unmodified 3. Perform a fast file copy using `mmap(2)` 4. Use `mmap(2)` to share memory among processes --- class: center, middle # Get Started!