Homework 5 Solutions

22C:116, Fall 2001

Douglas W. Jones
  1. The Problem: Give detailed pseudocode for a user-level implementation of demand paged virtual memory. No, that's too hard, so we'll break it into pieces:

    Part A: Give definitions for the functions needed to break a virtual address into the component fields relevant to this problem and to construct a virtual address from these component fields.

    Let's assume that somethinbg like the following masks are available:
    wordfieldbits = 8
    wordfieldmask = ... 0000000011111111 = (1 << wordfieldbits) - 1
    pagefieldmask = ... 1111111100000000 = ~ wordfield = (-wordfield) - 1
    
    These are given in binary above, and we have assumed, arbitrarily, for the sake of example, that there are 256 words per page.
    wordfield(va) = va & wordfieldmask
    pagefield(va) = va >> wordfieldbits
    makeva( word, page ) = (page << wordfieldbits) | word
    

    Part B: What is the best choice of page replacement policy for this system? LRU would be nice, but the system services you have available do not give sufficient information for that.

    We cannot do LRU because we have no accounting method to measure the time of use of a page. We cannot do clock replacement because we have no way to invalidate a page while leaving it in memory. We can, however, use soft page faults to detect attempts to modify a page, and this allows us to schedule pages for cleaning.

    If a page holds read-only data or pure code, the resulting policy will be effectively random, or at least, no better than random. We can, however, gain the advantage of a software clock policy for pages of read-write data.

    In sum, we can't use any really good policy, but we won't be quite as bad as random.

    Part C: Give a detailed description of any data structures your code must maintain. These would be initialized by make-virtual, if you bothered to write the code for it (you are not to do so).

    We need an array to serve as a software page-table for the range of pages between va1 and va2 inclusive:
       firstpage = pagefield(va1)
       lastpage = pagefield(va2)
       numpages = (lastpage - firstpage) + 1
    
       clockhand = 0 (initially)
         -- this is really clumsy!  we're using the page table
         -- as a frame table, so there are lots of table entries
         -- we have to skip before we find one that's relevant.
    
       pagetablentry pagetable[numpages]
    
    Each page table entry will have the following fields
       rights
    
    In this table, the rights are read_only, read_write, and unmapped, and they tell the state of the page. Note that all pages in the mapped range are logically read write pages of the open file fd. Therefore, we can conclude the following:
       rights = unmapped
          -- the page is in sector pagefield(va) - firstpage of the file
       rights = read-only
          -- the page is in main memory and is clean
       rights = read-write
          -- the page is in main memory and is dirty
    

    Part D: Give detailed pseudocode for your user-level page fault handler; it should use the data structures you outlined, and it should implement the policy you selected.

    faulthandler:
       if savarea.cause not one of addr_fault or access_fault,
          this is not a page fault,
          do other things not related to this assignment
       else
          page = pagefield(savearea.va)
          if page < firstpage or page > lastpage
             this is outside the scope of this assignment
          else
             sector = page - firstpage
             if pagetable[sector].rights = read_only
                -- soft page fault
                note savearea.cause should be access_fault!
                pagetable[sector].rights = read_write
                map(savearea.va, read-write) 
                return_trap(savearea)
             else
                -- real page fault
                note pagetable[sector].rights should be unmapped!
                note savearea.cause should be addr_fault!
                while map(va,read_write) = false
                   -- really clumsy clocklike replacement
                   while pagetable[clockhand].rights = unmapped
                      clockhand = (clockhand + 1) mod numpages
                      -- because we're using the page table, we have
                      -- to skip lots of unmapped table entries
                   if pagetable[clockhand].rights = read_write
                      -- clean a page
                      pagetoclean = makeva(clockhand + firstpage), 0)
                      write pagetoclean to sector clockhand of file fd
                      map(pagetoclean, read_only)
                      pagetable[clockhand].rights = read_only
                      clockhand = (clockhand + 1) mod numpages
                   else
                      -- return a clean page to the system
                      unmap(makeva(clockhand + firstpage),0))
                      pagetable[clockhand].rights = unmapped
                      clockhand = (clockhand + 1) mod numpages
                   endif
                endloop
                -- we finally mapped the required virtual address
                read makeva(page, 0) from sector of file fd
                map(savearea.va,read_only)
                pagetable[sector].rights = read_only
                -- it's now a clean page
                return_trap(savearea)
             endif
          endif
       endif
    

    Part E: Given the code you wrote, and given that there are multiple processes running the same code competing for a fixed pool of page frames in main memory, does your code allow processes to share in a controlled manner or does it allow a process to take and keep an unfair fraction of the page frames even though it has a much smaller working set. If the former, how, if the latter, what prevents fair sharing of page frames?

    The example code given as an answer to part D returns page frames to the system when it needs a new page frame. As a result, if other processes are waiting for page frames, the returned page frames have a chance of being given to those processes. Because of this, page frames may indeed be assigned to one process at one time and to another process at another time.

    However, the only time a process gives up page frames is when it needs a frame itself. As a result, if a process gets an unfair number of page frames and then has no more page faults, there is no way for the system to get some of those frames and give them to other processes. Therefore, although this solution does allow some sharing of page frames between processes, it cannot be characterized as fair, merely interesting.