xv6, line by line
lab 15
Lab 1515 mmap and munmap of files

Lab 15 · reveal · 18 steps · 10 commits

mmap and munmap of files: the reference solution

In this tree a process reaches a file’s bytes only through read and write: the kernel copies them between the buffer cache and a buffer the program owns. In this lab you add mmap, which puts part of a file into the address space: the program reads the file by loading from memory and changes it by storing to memory. munmap takes the mapping away again. Pages are read from the file only when they are first touched, and a shared mapping’s changes go back to the file when it is unmapped.

The system call is two lines in a man page; the kernel work touches almost every part of this tree. Where in the address space does a mapping go, when a heap already grows into the same empty space? How does a page fault know which file and which bytes a missing page stands for? Which pages were changed, and who noticed: the hardware, the kernel, or neither? Writing a page back to a file is a file-system operation: what does that require, and how much may one page cost? What do kfork, kexit and kexec, which knew nothing about mappings, now get wrong? And the fault handler now reads a file: what happens when the fault comes from inside a system call that already holds locks, perhaps the lock of the very file being mapped? The think section asks these questions in the order a designer meets them.

The reference solution is ten small commits. With it, touching 1 page of a 3-page mapping costs exactly 1 page of memory, and unmapping a 64-page mapping in which one page was changed writes one page back, not 64.

Each step shows one change on the branch ext/15-mmap, the code around it, and the state of the machine when that code runs.

The route
  1. 1A record per mapping kernel/proc.h
  2. 2mmap: check everything first kernel/mmap.c
  3. 3mmap: find room at the top, take a reference kernel/mmap.c
  4. 4The heap stops at the lowest mapping kernel/proc.c
  5. 5vmfault: is it in a mapping? kernel/vm.c
  6. 6mmapfault: refuse, then start from zeros kernel/mmap.c
  7. 7mmapfault: read the file's part, under the inode lock kernel/mmap.c
  8. 8munmap: free the pages, shrink the record kernel/mmap.c
  9. 9exit unmaps everything first kernel/proc.c
  10. 10exec unmaps the old image's mappings, after the point of no return kernel/exec.c
  11. 11A PTE bit the hardware sets kernel/mmap.c
  12. 12vmawrite: one page, one transaction, never past the end kernel/mmap.c
  13. 13The kernel's own writes set D by hand kernel/vm.c
  14. 14The child gets the mappings, not the pages kernel/proc.c
  15. 15The child's store reaches the file at its exit kernel/mmap.c
  16. 16Load before you lock kernel/mmap.c
  17. 17read, write and wait call it first kernel/sysfile.c
  18. 18Three processes, one file, three harts user/mmaptest.c

Keys: ← → step · Home start