[Edit: Found a page about the Minix 3 PM supporting my memory.]
When looking from today's perspective back, it's always important to as well look at usage of terms. 'MMU' and Memory Management are used interchangeable and most automatically include Address Translation and Memory Protection (often as well Paging). But that's a false shortcut based on modern hardware MMU providing all three (four) aspects at once.
Memory Management != Address Translation != Memory Protection
Memory management is strictly only assigning memory to processes, when they need some. Static or dynamic, whatever a system provides. This can quite well be done without any hardware (like DOS 360, classic Mac OS or OS/9 did). In fact, the segmented nature of the 8086 is quite supportive of memory management, providing basic address translation, eliminating all need for relocation (*1).
How Minix implemented it:
(this is about 8086, but AFAIK the basic working were the same for 80386, 68k and SPARC)
Minix 3 (before 3.2) had all memory management done within the PM server. The program map of a process after loading structured the segments as (low to high):
(It's important to note that this is about logical segments of a program, not 8086 segments)
- Text (Code)
Of these only
- Text, holding the program code,
- Data, holding all predefined data (including BSS) and
- Stack (I'm not sure about this)
were part of the program file. All others were generated from values of the program header (a.out). The amount of heap needed was calculated from the total memory requirement value.
When loading a program, a memory block was searched with a size to hold all file content (text+data) plus stack plus heap.
All these logical segments were put into a single 8086 segment, with CS=DS(=ES)=SS pointing to it. In Intel terminology the Tiny memory model. This was the default compile for all 8086 Minix up to 3.0.
In addition programs could be compiled with separation between text and data (Instruction and Data in Minix terms). In this case two 8086 segments were allocated, one for the code portion of a program file, the other for all remaining parts. In Intel speak that's the Small memory model. Small was made default with Minix 3.0
Segments in memory are only as large as they needed to be (usually smaller than 64 KiB) and continuous (not partitioned). If there is a separate Instruction segment (Small model), it may be shared. Swapping was done either in whole or not at all (there was a special case for shared code). Swapping was optional, not all systems used it - which is true for most 8086 Minix.
Minix 3.2 added a VM server and memory management was in turn moved from PM to VM.
There was no protection of any kind.
Any program using
- a pointer past its allocated 8086 segment(s), or
- changed the 8086 segment register value
was able to access and manipulate arbitrary memory, including the OS or its servers.
But what if a program needs more memory?
Bad luck. Basic Minix did not allow any resizing.
When a process is requesting more memory, it's taken, bottom up, from the prior assigned Heap. The only check made is against the lower boundary of the stack. If a requested amount would cross into the stack segment, the call will fail.
Memory allocation within a process is thus restricted to whatever amount of Heap was reserved via the header value. The amount could be changed with the CHMEM command. It could not go past 64 KiB for (Text+)Data+BSS+Heap+Stack (or should, no idea if there that was checked at all).
Minix is a teaching tool, not a productive environment. It's all about showing how things work, not building a system surviving evil code. Doing such will add many bells and whistles that would make it hard to see the core functionality.
This is as well the reason why Minix didn't care to provide larger segments, or more than 1(2) segments. The addition of the Small memory model was only made to show ability and workings of shared code as a tool to save on memory - something way more important back in the days. Well and to introduce all the litte tripwires this included :)
In fact, it's the basic idea of Minix' micro kernel structure that only a bare minimum is provided. Additional features are to be added as services.
*1 - If Stephen Morse had added segment size registers and a basic supervisor mode, the 8086 would have had everything to run a segmented protected mode OS. Essentially the only serious shortfall one could attribute to the 8086 design.