Demand Paging

Lecture Notes for CS 140
Spring 2014
John Ousterhout

  • Readings for this topic from Operating Systems: Principles and Practice: Chapter 9.
  • Demand paging: not all of a process's virtual address space needs to be loaded in main memory at any given time. 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 table 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 table 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:
      MOV +(SP), R2
    • 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.
  • 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)
    • Ideally: paging produces a memory system with the performance of main memory and the cost/capacity of disk!
  • 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.
  • Clock algorithm (also called second chance algorithm): keep reference bit for each page frame, hardware sets the reference bit whenever a page is read or written. To choose page for placement:
    • Cycle through pages in order (circularly).
    • If the next 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.
  • Dirty bit: one bit for each page frame, set by hardware whenever the page is modified. If a dirty page is replaced, it must be written to disk before its page frame is reused.
  • The clock algorithm typically gives additional preference to dirty pages. For example, if the reference bit for a page is clear, but 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 are 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 that process's frames. This eliminates interference from other processes.
    • Unfortunately, per-process replacement creates a new scheduling dilemma: how many page frames to allocate to each process? If this decision is made incorrectly, it can result in inefficient memory usage.
    • Most systems use global replacement.