Demand Paging

Lecture Notes for CS 140
Spring 2019
John Ousterhout

  • Readings for this topic from Operating Systems: Principles and Practice: Chapter 9.
  • Overall goal: make physical memory look larger than it is.
    • Locality: most programs spend most of their time using a small fraction of their code and data.
    • Keep in memory the information that is being used.
    • Keep unused information on disk in paging file (also called backing store, or swap space)
    • Move information back and forth as needed.
    • Ideally: paging produces a memory system with the performance of main memory and the cost/capacity of disk!
  • Demand paging: each page can be either:
    • In memory (physical page frame)
    • On disk (backing store)

Page Faults

  • What happens when a process references a page that is in the backing store?
    • For pages in the backing store, the present bit is cleared in the page map entries.
    • If present is not set, then a reference to the page causes a trap to the operating system.
    • These traps are called page faults.
    • To handle a page fault, the operating system
      • Finds a free page frame in memory
      • Reads the page in from backing store to the page frame
      • Updates the page map entry, setting present
      • Resumes execution of the thread
  • How does the OS figure out which page generated the fault?
    • x86: hardware saves the virtual address that caused the fault (CR2 register)
    • On earlier machines OS got only address of faulting instruction, must simulate the instruction and try every address to find the one that generated the fault
  • Restarting process execution after a page fault is tricky, since the fault may have occurred in the middle of an instruction.
    • If instructions are idempotent, just restart the faulting instruction (hardware saves instruction address during page fault).
    • Non-idempotent instructions are more difficult to restart:
      pushl %eax
      
    • Without hardware support it may be impossible to resume a process safely after a page fault. Hardware must keep track of side effects:
      • Undo all side effects during a page fault?
      • Save info about side effects, use it to restart instruction "in the middle"

Page Fetching

  • Once the basic page fault mechanism is working, the OS has two scheduling decisions to make:
    • Page fetching: when to bring pages into memory.
    • Page replacement: which pages to throw out of memory.
  • Most modern OSes use demand fetching:
    • Start process with no pages loaded, don't load a page into memory until it is referenced.
    • The pages for a process divide into three groups:
      • Read-only code pages: read from the executable file when needed.
      • Initialized data pages: on first access, read from executable file. Once loaded, save to the paging file since contents may have changed.
      • Uninitialized data pages: on first access, just clear memory to all zeros. When paging out, save to the paging file.
  • Prefetching: try to predict when pages will be needed and load them ahead of time to avoid page faults.
    • Requires predicting the future, so hard to do.
    • One approach: when taking a page fault, read many pages instead of just one (wins if program accesses memory sequentially).

Page Replacement

  • Once all of memory is in use, will need to throw out one page each time there is a page fault.
  • Random: pick any page at random (works surprisingly well!)
  • FIFO: throw out the page that has been in memory longest.
  • MIN: The optimal algorithm requires us to predict the future.
  • Least Recently Used (LRU): use the past to predict the future.
  • Implementing LRU: need hardware support to keep track of which pages have been used recently.
    • Perfect LRU?
      • Keep a hardware register for each page, store system clock into that register on each memory reference.
      • To choose page for placement, scan through all pages to find the one with the oldest clock.
      • Hardware costs prohibitive in the early days of paging; also, expensive to scan all pages during replacement.
      • No machines have actually implemented this.
    • Current computers settle for an approximation that is efficient. Just find an old page, not necessarily the oldest.
  • Two extra bits in each page map entry:
    • Reference: set by hardware whenever a page is read or written.
    • Dirty: set by hardware whenever the page is modified.
  • Clock algorithm (also called second chance algorithm). To choose page for placement:
    • Cycle through pages in order (circularly).
    • If the current page has been referenced, then don't replace it; just clear the reference bit and continue to the next page.
    • If the page has not been referenced since the last time we checked it, then replace that page.
    • If the dirty bit is set, don't replace this page now, but clear the dirty bit and start writing the page to disk.
  • Free page pool: many systems keep a small list of clean pages that are available immediately for replacement.
    • During replacement, take the page that has been in the free pool the longest, then run the replacement algorithm to add a new page to the free pool.
    • Pages in the free pool have their present bit off, so any references to those pages cause a page fault
    • If a page fault occurs for a page in the free pool, remove it from the free pool and put it back in service; much faster than reading from disk.
    • Provides an extra opportunity for recovery if we make a poor page replacement decision.
  • How to implement page replacement when there are multiple processes running in the system?
    • Global replacement:
      • All pages from all processes lumped into a single replacement pool.
      • Each process competes with all the other processes for page frames.
    • Per-process replacement:
      • Each process has a separate pool of pages.
      • A page fault in one process can only replace one of its own frames.
      • Eliminates interference from other processes.
    • Dilemma: how many page frames to allocate to each process? Poor choice can result in inefficient memory usage.
    • Most systems use global replacement.
  • What happens if memory gets overcommitted?
    • Suppose the pages being actively used by the current threads don't all fit in physical memory.
    • Each page fault causes one of the active pages to be moved to disk, so another page fault will occur soon.
    • Just run another thread?
    • The system will spend all its time reading and writing pages, and won't get much work done.
    • This situation is called thrashing; it was a serious problem in early demand paging systems.
    • Thrashing was a serious issue for timesharing machines with dozens or hundreds of users:
      • Why should I stop my processes just so you can make progress?
      • System had to handle thrashing automatically: stop running some processes for a while.
    • With personal computers, users can notice thrashing and kill some processes, so that others can make progress.
    • Memory is cheap enough that there's no point in operating a machine in a range where memory is even slightly overcommitted; better to just buy more memory.