|The Möbius Operating System: Memory Management|
This document describes the methods of memory management in The Möbius. The Möbius runs in a fully 32-bit environment with one flat 4GB address space per process. The size and layout of physical memory is handled by the kernel; in most cases, on current architectures, there will be less than 4GB of physical memory total. However, although the current kernel does not support 36-bit physical address extensions, the design does not put a practical limit on the amount of physical memory that can be installed in a Möbius system.
The organisation of memory by the kernel is virtually transparent from user mode. As far as the kernel is concerned, all user memory is managed in units of one page (4KB on the i386). There are three main ways of making memory accessible from user mode:
User-addressable memory covers most of the lower 2GB of the address space, from address 0x00001000 to 0x80000000. Note that the bottom 4KB is never mapped, and addresses in this range are never valid. This helps trap null pointer accesses. Memory that has been requested can be in one of three states:
Freshly-allocated memory starts out in the uncommitted state. Note that the kernel maintains two extra states for committed memory: clean and dirty. Memory starts out as clean and becomes dirty once it is modified. Clean pages can easily be regenerated (e.g. by loading from disk) and can be discarded, but dirty pages must be saved when they are swapped out.
Modules (EXEs and DLLs) are loaded into the process's address space by the kernel; a module's headers are loaded at the base address and the section data follow according to the layout specified in the headers. If a part of the memory required by a module is not available (e.g. it clashes with some other memory block), the module may be relocated. xxx - this is not currently implemented. The application's EXE image is the first to be loaded into memory, so in practice relocation is not necessary for the EXE.
Memory can be allocated at run-time with the VmmAlloc function, and freed with VmmFree. VmmAlloc will only allocate integral numbers of pages; it is up to the application programmer to split pages up if finer allocations are needed. There is a malloc implementation in libc that uses VmmAlloc, although programmers may use a different heap allocation routine if desired. VmmAlloc returns the base address of the allocated memory: it is possible to request a specific address for allocations, and if that address clashes, VmmAlloc will adjust it and return the adjusted base.
xxx - implement memory sharing and write about it
As far as kernel-mode code is concerned, memory management in kernel mode is much the same as in user mode. malloc/free and VmmAlloc and related functions are available, although there are various special considerations to take into account.
The main thing to remember is that all addresses above 0x80000000 are shared throughout all processes; otherwise it would make kernel code a lot less efficient. Any process-specific memory management for the kernel comes under the remit of the idle process (proc_idle); any addresses below 0x80000000 are accessible only within the idle process (i.e. by the idle thread and any other kernel threads).
The kernel heap (used by malloc and free in the kernel) is at a much lower level than in user mode. The kernel heap is a single contiguous block of virtual address space between 0xE0000000 and 0xF0000000; as such, it is limited to 256MB. This should not be a problem, especially since maximum kernel heap allocations should be much smaller than user-mode ones. If, in a driver, you need large blocks of virtual address space, use VmmAlloc (preferable allocating from the lower half of proc_idle's address space). The kernel heap is backed by normal, non-contiguous physical memory pages; they are mapped read-write and supervisor-only. Note that the kernel heap is managed separately to the virtual memory manager's memory.
The VMM API is more flexible when used directly from kernel mode. There is an additional function for allocating address space: VmmMap, which can perform every kind of mapping available (normal memory, shared memory, memory-mapped file, memory-mapped image, and virtual-to-physical mappings). VmmMap (on which VmmAlloc is based) will share blocks above 0x80000000 among all processes, and will restrict blocks below 0x80000000 to the calling process. It is not recommended that drivers allocate user-mode memory without the application's knowledge (although thread and process information blocks are allocated this way). Instead, it should be possible to allocate a region of memory either in the idle process or in the top half of the address space, share it (using VmmShare), and have applications attach to that memory using VmmMapShared. This is the technique used by video drivers to allow applications access to the video frame buffer.
The layout of virtual memory in The Möbius is as follows: