Overview
- paging
- virtual memory hardware
- minix memory management
Effective Paging
- paging only works well if most of the memory references are
cache hits not requiring disk access or page table access
- most programs exhibit locality: most of the accesses
are in a small group of pages (the working set of the program),
and the most-recently used pages are the most likely to be accessed again
- it is easy to design programs that have no locality, and context
switches also tend to destroy locality, but in general locality
is common
- when in need of space, one or more pages must be selected for
eviction from memory:
- ideally, the page that will be not be referenced the longest
should be evicted. This is optimal, but usually hard to compute
- as an approximation, the page that was accessed least recently
could be evicted (assuming past predicts future): Least Recently Used, LRU,
also somewhat hard to keep track of
- as an approximation to LRU, periodically mark all pages unused,
then see if they are used. If not, they are candidates for eviction:
Not Recently Used, NRU
- NRU only works if the hardware keeps track of page accesses.
- if the hardware does not directly keep track of page accesses, the OS
can unmap pages but retained in memory. Any access causes a
page fault. The OS can then mark the pages as used,
and the page fault can complete quickly
- to select a page in NRU, select the one that was loaded the
longest ago: FIFO or second chance (because an accessed
page gets a second chance to remain in memory)
Virtual Memory Hardware: Pentium
- support for pure segmentation, pure paging, or combinations of
both
- pentium has six 16-bit segment registers, each containing a 13-bit
segment selector, 1 bit to report whether it is a local or global segment
selector (i.e. whether the segment descriptors are in the LDT or GDT),
and two privilege bits
- segment registers are selected automatically during memory operations,
e.g. the CS (code segment) for all program loads (IP/PC), e.g. next
instruction, jump
- a segment selector is an index into a table of 8K 64-bit (8 byte) entries
which include a 32-bit base field, a 20-bit limit field (plus a bit which
scales it by 212 to give up to 232 limits), and other
page-table kind of bits (in memory, type and protection, etc)
- a program address ("offset" -- what most people call a virtual
address) is added to the base (as long as it does not exceed the limit)
to give a "linear address".
- if paging is disabled (as set by a bit in the CPU), the linear address
is used as the physical address for the access
- otherwise, the top 10 bits of the linear address are used as an
offset into a 1024-entry page directory, containing a pointer
to a 1024-entry page table
- the next 10 bits of the linear address are used as an offset into
the page table, the contents of which are added to the last 12 bits of
the linear address to give the actual physical address
- this is only practical when programs have locality and the TLB
can map dir and page combinations to physical addresses
- the segment computation (checking against limit, adding base) can be
done within the CPU on every access, no need for memory accesses
- two protection bits in the segment selector contain the level
of the segment -- accesses
to other segments on the same level is fine, accesses to other segments
on other level is more restricted
Operating System Protection Structure
- a program has only controlled access to inner rings of
the system, for example through a call gate, similar to
an interrupt vector table -- no option to jump into the middle of
kernel code
- innermost ring might contain kernel, next ring the system calls,
then the libraries, and finally the user code (p. 419, figure 4-29)
Minix Memory Management
- segments, including swapping, but no paging
- program manager allocates memory using first fit, never moves it
or changes it
- program manager keeps track of available memory and assigns it
to processes (policy), kernel does the actual memory
mapping (mechanism)
- program manager allocates memory for fork (copy of the
parent is the child) and for exec (memory is returned, new
memory is allocated)
- two modes (program-dependent):
- combined I and D, meaning the code and data are in the same segment, or
- shared text, meaning the code is in a separate segment that can
be used by multiple processes (e.g. parents and children)
- for the second case, exec searches to see if the required
text is already loaded and can be shared
Minix Process Manager
- main, on page 875, looks like every other event-driven
reactive process we've seen so far -- kernel tasks and device drivers
- the loop dispatches on call_nr, extracted from the message
- system calls (p. 873, but you know them from project 3)
include fork, exit, wait, brk, exec, system calls
related to signals, and a few miscellaneous calls such as getpid and
setuid
- process manager has its own process table (p. 870), including:
- the process segments (line 17609) -- all addresses must be a
multiple of the click size (1024, 0x400), and each segment
has a physical address, a virtual address, and a length
(in include/minix/type.h, line 02829 on p. 669)
- various process and user IDs (lines 17612-17625)
- sharing information for shared text
- signal handling information
- to maximize portability, Minix does not use hardware
segmentation to detect stack overflows: stack overflow is only
detected if the program does an s/brk call and the stack has
already overflowed
Minix Memory Allocation
- the hole table, defined in
include/minix/type.h (should be on p. 670, but omitted from book)
keeps track of available memory, again in clicks. Allocated memory is kept
track of in the program manager's process table. Each entry has a pointer
to the next entry (may be NIL), and the base and length of the hole
- the first-fit allocation algorithm is implemented in file
servers/pm/alloc.c, not in book
- freeing memory is more complex because:
- there may not be any available hole descriptors (128 are
allocated, Minix does not store the hole descriptors in the holes themselves)
- the free list is kept in sorted order, so the new block must be
inserted in the correct position
- the newly freed memory may be adjacent to one or two free blocks,
and if so must be merged (lines 127-146 and merge starting on line
172)
- merging requires seeing if this block is adjacent to the one
before, and if so, merging them, and then repeating the operation
with the next block

This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 2.5 License.