## Basic Memory Management Functions

An important OS-9 function is memory management. OS-9 automatically allocates all system memory to itself and to processes, and also keeps track of the logical contents of memory (meaning which program modules are resident in memory at any given time). The result is that you seldom have to be bothered with the actual memory addresses of programs or data.

Within the address space, memory is assigned from higher addresses downward for program modules, and from lower addresses upward for data areas, as shown below:

                    +---------------------------+  highest address
                    !       program modules     !
                    !         (RAM or ROM)      !
                    !                           !
                    ! - - - - - - - - - - - - - !
                    !                           !
                    !        unused space       !
                    !       (RAM or empty)      !
                    !                           !
                    ! - - - - - - - - - - - - - !
                    !                           !
                    !         data areas        !
                    !           (RAM)           !
                    !                           !
                    +---------------------------+  lowest address (0)

### Loading Program Modules Into Memory

When performing a fork operation, OS-9's first step is to attempt to locate the requested program module by searching the “module directory”, which has the address of every module present in memory. The 6809 instruction set supports a type of program called “reentrant code” which means the exact same “copy” of a program can be shared by two or more different processes simultaneously without affecting each other, provided that each “incarnation” of the program has an independent memory area for its variables.

Almost all OS-9 family software is reentrant and can make most efficient use of memory. For example, Basic09 requires 22K bytes of memory to load into. If a request to run Basic09 is made, but another user (process) had previously caused it to be loaded into memory, both processes will share the same copy, instead of causing another copy to be loaded (which would use an additional 22K of memory). OS-9 automatically keeps track of how many processes are using each program module and deletes the module (freeing its memory for other uses) when all processes using the module have terminated.

If the requested program module is not already in memory, the name is used as a pathlist (file name) and an attempt is made to load the program from mass storage (see the section called “Executable Program Module Files”).

Every program module has a “module header” that describes the program and its memory requirements. OS-9 uses this to determine how much memory for variable storage should be allocated to the process (it can be given more memory by specifying an optional parameter on the shell command line). The module header also includes other important descriptive information about the program, and is an essential part of OS-9 operation at the machine language level. A detailed description of memory modules and module headers can be found in the OS-9 System Programmer's Manual.

Programs can also be explicitly loaded into memory using the load command. As with fork, the program will actually be loaded only if it is not already in memory. If the module is not in memory, OS-9 will copy a candidate memory module from the file into memory, verify the CRC, and then, if the module is not already in the module directory, add the module to the directory. This process is repeated until all the modules in the file are loaded, the 64K memory limit is exceeded, or until a module with an invalid format is encountered. OS-9 always links to the first module read from the file.

Level One systems load modules on 256-bytes page boundaries. Level Two system load modules contiguously on memory block boundaries. The block size is usually 2K per block for systems equipped with MC6829 MMUs, or 4K bytes for most SS-50 bus systems. Free memory to be used for user data area need not be contiguous because the MMU can map scattered free blocks th be logically contiguous. Since OS-9 will request the largest physically contiguous memory block available (up to 56K) to load program modules, load operations can fail even if sufficient total free memory exists. Any of this memory not used by the load operation is returned to the system.

If the program module is already in memory, the load will proceed as described above, loading the module from the specified file, verifying the CRC, and when attempting to add the valid module to the module directory, noticing that the module is already known, the load merely increments the known module's link count (the number of processes using the module.) The load command can be used to “lock” a program into memory. This can be useful if the same program is to be used frequently because the program will be kept in memory continuously, instead of being loaded repeatedly.

The opposite of load is the unlink command, which decreases a program module's link count by one. Recall that when this count becomes zero (indicating the module in no longer used by any process), the module is deleted, e.g., its memory is deallocated and its name is removed from the module directory. The unlink command is generally used in conjunction with the load command (programs loaded by fork are automatically unlinked when the program terminates).

On Level Two systems, multiple modules loaded from a single file are logically associated by the memory management logic. All modules in the group will occupy contiguous physical memory blocks. The group's memory can only be deallocated when all the modules which are members of the group have zero link counts. Similarly, linking to one module within a group causes all other modules in the group to be mapped into the process's address space. (see the section called “Executable Program Module Files”).

Here is an example of the use of load and unlink to lock a program in memory. Suppose the copy command will be used five times. Normally, the copy command would be loaded each time the copy command is called. If the load command is used first, copy will be locked into memory first, for example:

    OS9: load copy
    OS9: copy file1 file1a
    OS9: copy file2 file2a
    OS9: copy file3 file3a
    OS9: unlink copy

It is important to use the unlink command after the program is no longer needed, or the program will continue to occupy memory which otherwise could be used for other purposes. Be very careful not to completely unlink modules in use by any process! This will cause the memory used by the module to be deallocated and its contents destroyed. This will certainly cause all programs using the unlinked module to crash.

### Loading Multiple Programs

Another important aspect of program loading is the ability to have two or more programs resident in memory at the same time. This is possible because all OS-9 program modules are “position-independent code”, or “PIC”. PIC programs do not have to be loaded into specific, predetermined memory addresses to work correctly, and can therefore be loaded at different memory addresses at different times. PIC programs require special types of machine language instructions which few computers have. The ability of the 6809 microprocessor to use this type of program is one of its most powerful features.

The load command can therefore be used two or more times (or a single file may contain several memory modules), and each program module will be automatically loaded at different, non-overlapping addresses (most other operating systems write over the previous program's memory whenever a new program is loaded). This technique also relieves the user from having to be directly concerned with absolute memory addresses. Any number of program modules can be loaded until available system memory is full.

### Memory Fragmentation

Even though PIC programs can be initially loaded at any address where free memory is available, program modules cannot be relocated dynamically afterwards, e.g., once a program is loaded it must remain at the address at which it was originally loaded (however Level Two systems can “load” (map) memory resident programs at different addresses in each process' address space). This characteristic can lead to a sometimes troublesome phenomenon called “memory fragmentation”. When programs are loaded, they are assigned the first sufficiently large block of memory at the highest address possible in the address space. If a number of program modules are loaded, and subsequently one or more modules which are located in between other modules are “unlinked”, several fragments of free memory space will exist. The sum of the sizes of the free memory space may be quite large, but because they are scattered, not enough space will exist in a single block to load a program module larger than the largest free space.

The mfree command shows the location and size of each unused memory area and the mdir e command shows the address, size, and link (use) count of each module in the address space. These commands can be used to detect fragmentation. Memory can usually be de-fragmented by unlinking scattered modules and reloading them. Make certain none are in use before doing so.