Memory Management: Allocation, Swapping, and Paging
Minix System Task
coordination between the servers and the kernel is essential
in Minix, kernel functions needed by the server but not associated
with an I/O device are placed in the system task
like all tasks, the system task receives messages, executes appropriate
code, and returns results, without blocking
a special call, cause_sig (p. 740), sends a message
conditionally, that is, only if the memory manager is ready to
receive the signal -- otherwise sets a flag and lets the memory manager
discover that it missed a call
a number of calls perform the kernel portion of process management
system calls:
fork,
exec,
exit,
times,
kill.
other calls perform tasks on behalf of the memory manager or file
server, for example mapping memory to a process, returning the stack
pointer of a process (stored in the kernel process table), copying
data between processes, returning the next free memory, converting
virtual to physical addresses, tracing a process execution,
and rebooting or shutting down the system
Supporting Fork and Exec
fork (p. 729) requires copying the proc table entry (the MM has already
selected the child process ID), changing the return value for the
child (which is in the stack), and clearing the usage times
the resulting process is not yet mapped, so is not scheduled
mapping takes the memory map provided by the memory manager and
initializes all the corresponding segments in the process table, then
schedules the process. The segments will be loaded on return from
interrupt, i.e. when the process actually gets started
exec resets the stack pointer and places a new PC in
the process table, then readies the process for execution. Someone
else must map the new code segments to the memory before calling this
code
exit does bookkeeping and removes the process from any queues for
any messages it was trying to send, then cancels signals and sets the
slot to free -- someone else is responsible for closing file descriptors,
deallocating memory, etc
Signal Handling
to send a signal to a process, must:
save all the process state (done by the context switch code)
create new state for execution of the signal handler, on the
same stack (lines 15176 and following)
put a return address that will lead to do_sigreturn,
to allow for cleanup of the stack
restore the process state (done by the context switch code)
Virtual Memory in Minix
umap, p. 742
a virtual address v corresponds to a physical address p based
on two per-segment fields, physical-base and virtual-base (possible segments
include, text, data, and stack)
v - virtual-base is the offset o into the segment
p = o + physical-base is the physical address
computation on lines 15686-15690
Rebooting
rebooting is done by keyboard code, since the monitor must
be able to receive input from the user
Memory Management
a single program in memory can occupy all the space not used
for the OS or the ROM
memory can be shared among a number of tasks using fixed partitions
each executable must be either location-independent code, or
relocatable code relocated at load time
each area of memory must also be protected by matching bits for
the memory area to bits in the CPU that user programs cannot modify
relocation can also be achieved (in hardware) by using two
segment registers, base and limit: on each access,
the CPU checks the address against the limit, then adds the base
for protection, only the OS can change the base and limit registers
Keeping track of memory
memory is divided into fixed-sized units
each unit is allocated or free
two ways to keep track of the allocated/free memory:
bitmap: each bit is 0 if the corresponding unit is free, or 1 if
it is taken. For units of n bits, the bitmap takes up 1/n
of the memory, e.g. for n = 215 bits
and 1GB = 233 bits of memory, the bitmap takes
218 bits or
32KB = 215 bytes.
linked list: each allocated or free segment is stored in a list.
When a segment is freed, adjacent list entries must be merged, when a
segment is allocated, an existing entry must be changed or split
first fit: first free block that fits is split and used. Fast,
fairly good with regard to fragmentation, may waste memory
next fit: same as first fit, but start from the end of the last
search, and wrap around. Almost the same performance as first fit
best fit: search the entire list to find the smallest hole
that will fit. Works great if exact matches are found (i.e. if allocations
are a few different sizes), otherwise leaves unusably small holes.
worst fit: use the biggest free block. makes it hard to ever
allocate large blocks.
quick fit: keep free lists for commonly requested sizes --
but merging after deallocation is expensive
Memory on Disk
if the OS needs more memory and doesn't have it available, it can
copy something to disk
any process trying to access the memory that is copied to disk
will have to wait for it to be brought back from disk
a process can be swapped to disk in its entirety, and then
of course its execution is suspended, only to be resumed when the
process is swapped in
or, a process's memory can be divided into fixed-sized blocks
(pages), each of which can be written to disk while not in use
when the disk is copied back to memory, it may be in a different
location:
this is easy if an entire segment is copied back in and
segment registers are in use -- only the base register needs to be
updated
if pages are used, essentially a segment base register
is needed for each page (since the size is fixed, no limit register is
needed) -- the collection of these "segment base registers" is called
the page table
page tables (one for each process) are kept in memory while any
part of the process is in memory
Fast Paging
the program computes an address and requests the corresponding
virtual memory location
the Memory Management Unit (MMU) is the hardware that
translates the virtual address to a physical address by
reading the page table from memory as needed
the page table entry must contain the physical address of the
page frame corresponding to the given virtual page, and additional
bits to record:
whether the translation is valid (mapped)
whether the page may be written (and/or read or executed)
whether the page has been read (referenced) since this bit was cleared
whether the page has been written (modified, made dirty) since this bit
was cleared
whether the page may be cached
dirty pages will have to be written back to disk, whereas
pages that already have a copy on disk but are not dirty can be
discarded
a small associative memory, the Translation Lookaside
Buffer (TLB), caches translations (and other bits) for
recently used pages
some RISC computers require the OS to manage the TLB
Efficient Paging
keeping a page table entry for every page of a process's
virtual address space can be expensive
one answer is to keep a top-level page table, and have pointers
to second-level page tables for only those areas that are allocated
another answer is to keep a table of physical-to-virtual address
translations instead: this table takes up a fixed fraction of the
memory (one entry per physical page), and is called an inverted
page table
this efficiency is important as virtual address spaces grow,
e.g. to 64 bits
inverted page tables must be searched (perhaps using hashing)
when a new TLB translation is needed