Paging

Operating Systems — Lecture 05

Three numbers

$ grep ^pgfault /proc/vmstat
pgfault 9687532485                     17 days: 6,600 a second

$ ps -eo rss= | awk '{ s += $1 } END { print s / 1024 " MB" }'
58339 MB                               on a machine with 61 GB, 41 in use

$ swapon --show
/dev/dm-1 partition  80G 834.2M        memory that is not in memory

Lecture 04: physical memory arrives in a page fault. This lecture: what happens inside one.

The story

Two views of one address space

$ grep -c . /proc/$$/maps                           33 zones
pages: 2647  present: 1098                          from VmSize and VmRSS

Two edits

                                       VSZ   RSS  MINFL
after mmap(4 MB)                      6848  1692     64      the list
after writing byte 0 of every page    6848  5788   1088      the table, one fault each
after writing byte 4095 of every page 6848  5788   1088      nothing

Inside a page fault

Four verdicts

verdict the address is the frame the process sees
minor fault in a zone, allowed found in memory nothing, ~1 µs
major fault in a zone, allowed read from disk a delay, ms
SEGV_MAPERR in no zone – SIGSEGV
SEGV_ACCERR in a zone that forbids it – SIGSEGV
$ ./segfault 6        SIGSEGV at 0x7dd401855000: SEGV_ACCERR: the zone forbids this access
$ ./segfault 3        SIGSEGV at 0x755bca209000: SEGV_MAPERR: no zone covers the address
$ ./major_fault       first pass (from disk):    16384 major faults, 0.302 s
                      second pass (from cache):   1024 minor faults, 0.002 s

Where frames come from

Swap, in a box

$ systemd-run --user --scope -p MemoryMax=64M ./swap
start:                   RSS      1 MB   swap      0 MB   major faults      0   0.00 s
after writing 256 MB:    RSS     63 MB   swap    193 MB   major faults      3   0.62 s
after reading it back:   RSS     64 MB   swap    192 MB   major faults   8424   2.19 s

Without the box: 257 MB resident, 0 major faults, 0.00 s.

The program never knew. It only got slower.

One frame, many tables

62c63257a000-62c6326b0000 r-xp  /usr/bin/bash    Rss: 1240 kB   Pss:   63 kB
62c66092d000-62c660f62000 rw-p  [heap]           Rss: 6228 kB   Pss: 6228 kB

Ten processes, four ways

ten processes from USS each PSS each RSS each real total
one static executable 52 KB 115 KB 708 KB 1.2 MB
one dynamic executable 100 KB 104 KB 1,688 KB 1.1 MB
ten static executables 708 KB 708 KB 712 KB 7.1 MB
ten dynamic executables 116 KB 120 KB 1,692 KB 1.2 MB

RSS: once per process. PSS: split, sums to the truth. USS: only what nobody else maps.

Copy-on-write

child  after fork():         Pss 32839 kB  Shared_Dirty 65616 kB
child  reads every page:         0 page faults
child  writes to every page: 16384 page faults

A table too big

flat: 2^35 entries x 8 B = 256 GB per process      VmPTE: bash 60 kB, chrome 7812 kB

A table too slow

$ sudo perf stat -e dTLB-load-misses,dTLB-loads ./tlb         1,488,442 misses   91.9%   6.5 ns
$ sudo perf stat -e dTLB-load-misses,dTLB-loads ./tlb huge       13,569 misses   53.9%   4.9 ns

The page fault is where the kernel does its memory work.

It decides, from the list of zones, between fixing and killing; it brings frames from zero, from files and from swap; it shares frames between processes and copies them only on a write.

The page table makes it affordable: a tree, so that it fits, and a TLB, so that it is rarely read.