The Möbius Operating System: Memory Management

Memory Management

Tim Robinson


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.

2.User Mode

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:

  • Allocate it at load time in an EXE or DLL
  • Allocate it at run time using VmmAlloc
  • Map it from another process using xxx - memory_sharing_function

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:

  • Uncommited: the memory has never been referenced. No physical storage (RAM or swap space) is set aside. The memory will have to be committed before it can be used.
  • Committed: the memory has been read from or written to at least once, and it is currently resident in RAM. The memory is available for use immediately.
  • Paged out: the memory has been committed but it is no longer present in RAM. Its contents have either been written to the swap file or the associated memory-mapped file, or, in the case of unmodified memory-mapped pages (which can be reloaded from the original file) have been discarded. The memory will have to be read from disk before it can be used.

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.

Note: Although VmmFree accepts a void* parameter, it will only accept addresses within blocks that were allocated using VmmAlloc.

xxx - implement memory sharing and write about it

3.Kernel Mode

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:

  • 0 to PAGE_SIZE: inaccessible
  • PAGE_SIZE to 0x80000000: available to user applications. Thread and process information blocks are allocated at the bottom end; executable images are loaded where appropriate; thread stacks are loaded at the top of the user space.
  • 0x80000000 to 0xC0000000: driver images and space for kernel-mode VmmAlloc blocks
  • 0xC0000000 to 0xD0000000: space reserved for the kernel image
  • 0xD0000000 to 0xDFC00000: available for kernel-mode VmmAlloc blocks
  • 0xDFC00000 to 0xE0000000: kernel stacks
  • 0xE0000000 to 0xF0000000: kernel heap
  • 0xF0000000 to 0xF8000000: identity mapping of the first 128MB of system physical memory
  • 0xF8000000 to 0xFFC00000: space for temporary mappings of physical memory
  • 0xFFC00000 to end of memory: current process's page directory and page tables

Post a comment