Linux's page cache doesn't use a single LRU list -- it uses TWO: an
inactive list and an active list. New pages start on inactive.
Reclaim (evicting pages under memory pressure) always scans inactive
first, never touching active. A page only earns promotion to active --
and the real protection that comes with it -- after a second reference
while it's still sitting on inactive.
This is deliberately different from a single-list LRU (compare the
Database Internals course's "LRU Buffer Pool" exercise, which is exactly
that): the point of splitting into two lists is scan resistance. A
one-off sequential scan (grep -r walking a huge source tree) touches
millions of pages exactly once each -- with a single LRU list, that scan
would evict your entire actually hot working set, since plain
"most-recently-used" can't distinguish a genuinely hot page from a page
touched once by an unrelated scan. Requiring a second touch before a page
earns real protection fixes that.
TwoListLRU in the starter code passes basic get/put smoke tests, but
a teammate noticed something worse under load: pages that are clearly
"hot" (repeatedly re-read) sometimes get evicted anyway, while pages
touched exactly once during a one-time scan stick around indefinitely --
the opposite of what scan resistance is supposed to guarantee. Find and
fix the bug in _touch.
Constraints
- The fix is local to
_touch--get,put, and_reclaimare already correct. - A page's first reference while on
inactivemust NOT promote it -- it should only be flagged (added toself.referenced). Promotion happens on the reference AFTER that, while the page is still oninactive. - A page already on
activestill just refreshes its position on any reference (this part of_touchis correct in the starter).
Real-world grounding
- The active/inactive list split and its reclaim-scans-inactive-first
policy:
mm/vmscan.c(seeshrink_active_list(), and theLRU_ACTIVE/LRU_INACTIVElist enum used throughout). - The "second reference promotes" logic (
folio_mark_accessed(), which sets aPG_referencedflag on a first hit and only moves the folio to the active list on a subsequent one):mm/folio.c
Real Linux adds more refinements on top of this same two-list skeleton -- separate LRU lists per memory cgroup and NUMA node, a distinction between file-backed and anonymous pages, and (in very recent kernels) a multi-generational LRU that generalizes "two lists" into several generations -- but the core defining rule this exercise tests (single touch does not protect a page; a second touch does) is the same one that motivated the original active/inactive split.