Address Translation
A user process can reside in any part of the physical memory. The step from logical address to physical address is called "address translation" - handled by MMU.
MMU is protected by kernel mode - in kernel mode, OS has unrestricted access to both OS memory and users' memory - this allows OS to load users' program into users' memory, to dump out a program/process, to access and modify parameters of system calls, and so on.
Let's look at a very primitive way of implementing MMU: base register (smallest legal physical memory address), plus the limit register (size of the range). In the textbook, please look at Fig. 8.2, then, Fig. 8.1. The base and limit registers can be loaded only by the OS, and the instructions to do so can only be accessed in kernel mode.
Segmentation Method
MMU can manage address translation with different assumption, or policy. Segmentation based methods directly borrows from the concept of how a process address space is broken up as text segment, data segment, heap, stack, etc. Each segment will then be assigned its own base and limit registers.Simple segmentation method has a few problems.
- Not every process is the same size
- Over time, memory space becomes fragmented
- Really bad if want space to grow dynamically (e.g. heap)
- Doesn't allow heap and stack to grow independently
- Want to put these as far apart in virtual memory space as possible so that they can grow as needed
It is also hard to do inter-process sharing, may need to move multiple times to fit everything, and limited options for swapping to disk. We also need to realize that sometimes it should be OK and is OK to adress outside valid range, such as when using the stack.
Fragmentation wastes space. External fragmentation has free gaps between allocated chunks, while internal fragmentation happens in situation when we actually don't need all memory within the chunks already allocated.
Translation: a process generates logical addresses that it needs to access, a correct program should never address gaps except for the several scenarios allowed. If a process does address gap in an "illegal" way, MMU traps the process to kernel.
Translation table also needs protection modes. For example, code segment would be read-only. Data and stack would be read-write (stores allowed). Shared segment could be read-only or read-write.
Page Method
Solution to fragmentation - well solution to external fragmentation is to allocate physical memory in fixed size chunks ("pages"). Every chunk of physical memory is equivalent. Also, by making the pages small enough, internal fragmentation is indirectly handled as well. Typical page sizes are within 1K to 16K bytes. Due to that, a code segment taking up 8 pages (8KB/page) is quite normal. We can use a simple vector of bits to index pages, e.g. 00110001110001101..., with each bit representing whether a page is allocated (1-allocated, 0-free).
Each process has its own page table, which contains physical page and permission (valid bits, read, write permission, etc.) for each virtual page. Address translation is done via mapping from logical to physical page, and then offsets from beginning of the page.
Now let's consider a real case - processes will have highly sparse and widely distributed set of virtual addresses, and the operating system cannot afford to use too much resources to store the page table, e.g. as a large contiguous array. A typical solution is to use a hierarchy (tree) structure to organize page tables. On x86 processors, the levels are called PGD, PMD and PTE (page global directory, page middle directory and page table entries). These levels and also the offset within each page together use up all the addressing bits. Linux2.6 does not use the PMD mode, 10 bits for PGD, 10 bits for PTE and 12 bits for each page.
Note - page is primarily a term that applies to logical addresses. When it comes to physical address, for that matter, physical memory, the corresponding term is frame. Page size and frame size are always the same. OS maintains a system wide frame table, for frames that are allocated and records to which process, which page, the page is associated. OS maintains for each process a page table, just like how it maintains register contents and instruction counters. Where are these contents? PCB.
The simplest way to implement page table in hardware is to use special registers. PDP-11 has 16-bit addresses, each page is 8KB. That means we need 8 of such registers, one for each page. Systems with larger page tables cannot afford to do that. Instead, page tables must reside in main memory. Let's think about this a bit, we need to manage memory using a table stored in memory?
PTBR (page table base register) maintains the pointer towards the active page table. Context-switch needs to handle updating PTBR. Page table is a complex construct, runtime performance is also critical. For this need, a speical hardware cache (which implements a key-value associative array) is implemented in hardware with 64 to 1024 entries. this cache is called TLB (translation look-aside buffer). Being a cache, TLB has all the functionality of a regular cache. Concepts like TLB hit/miss, replacement policy, all still apply. In addition, TLB can also be flushed, some TLB entries can be wired down.