[color="sienna"]When Linux boots, it starts with the MMU disabled, so initially it deals only with physical
memory. The kernel image is copied
to physical address 0x8000 in DRAM and executed. First a master page
table is created and placed just before the kernel, which describes all
available DRAM. The MMU is then switched on, mapping all of DRAM to
virtual address 0xC0000000. The kernel reserves 64K of virtual memory
for the interrupt handler code (usually at the very top of virtual
memory), and sets up all the mappings for the hardware registers (UART,
USB, LCD, GPIO, etc). Once kernel space is established and all drivers
are initialized, the linux kernel moves on to establishing user space.
This involves reading in the file system and actually executing
processes.
file:///C:/DOCUME%7E1/ljy/LOCALS%7E1/Temp/moz-screenshot-1.jpg
[color="sienna"]Each process that runs on the system does so in its own memory “context”. Each context
has its virtual memory space
established and maintained by the kernel using a separate page table.
So each process can “see” the entire user space, but its underlying
physical memory is different from the other processes. Multiple
processes and their contexts are run in time slices. This means that
each process executes for a time, then is halted, and execution is
passed to the next process in a rotating queue. The act of passing
execution is called “context switching” and its frequency varies,
depending on the kernel configuration and the current system load.
[color="sienna"]Each context includes the mappings for both user and kernel space because each new
page table is initialized with the
master page table contents. So, code executing in kernel space has full
access to the entire memory map for the current process. Switching back
and forth between kernel and user space execution does not require a
page table swap.
[color="sienna"]When a process is executed, the kernel creates a new context, and maps just enough
physical memory to copy the
executable image into DRAM (starting at least a page above virtual
address 0, because 0 is reserved for faults). The executable is broken
into the same sections as the kernel: code and data (uninitialized
global data, zero-initialized global data). Once the kernel schedules
the process for execution, it sets the stack to grow down from the top
of user space, and the heap to grow up from the end of the executable
image. This way the chances of the two spaces colliding is minimized.
Any dynamic libraries the process pulls in are placed at virtual
address 0x40000000.
[color="sienna"]The kernel reserves virtual memory whenever a process or driver requests it, but it doesn’t
actually map in the underlying
physical memory until the memory is accessed (copy-on-write). So, the
physical DRAM is used only when it absolutely has to be, and the kernel
controls the allocation through a global memory management scheme.
Ideally, the kernel tries to allocate contiguous blocks of physical
memory as contiguous memory is most likely to evenly fill physically
tagged caches. Thus, the kernel organizes memory to grow inward from
the ends of physical memory, maximizing contiguous memory in the
middle. The physical memory map is described in detail in the next
section.