home *** CD-ROM | disk | FTP | other *** search
- Memory Management on the Amiga
- by Fabbian G. Dufoe, III
-
- Because AmigaDOS is a multi-tasking operating system it requires a more
- complicated memory management scheme than most personal computer operating
- systems have. Single-tasking systems can get by with setting the upper and
- lower limits of the free memory region. With different processes starting
- and stopping independently free memory gets fragmented. The system has to
- know where to find each fragment if it's to use memory efficiently.
-
- AmigaDOS keeps a list of available memory. When a process needs RAM it
- asks the operating system to allocate some. The system searches the memory
- free list to see if there is a big enough block available. If so it
- allocates it to the process and removes it from the memory free list. When
- the process frees the memory the system returns it to the memory free list.
- The operating system doesn't keep track of which process allocated the
- memory, so if a process fails to return it there is no way to recover it
- without rebooting the system.
-
- Like everything in the Amiga's operating system, the path to the memory
- free list begins with a pointer to the exec library ExecBase structure. The
- ExecBase structure and all the structures and lists the system uses for
- memory management are defined in the include files listed in the ROM Kernel
- Exec Manual. These same files are supplied with your C compiler or
- assembler. They are also available in the Native Developer's Kit available
- >from Commodore Amiga Technical Support (CATS).
-
- Opening the exec library returns a pointer to the ExecBase structure:
-
- struct ExecBase *ExecBase;
- ExecBase = (struct ExecBase *)OpenLibrary("exec.library, 0L);
-
- The ExecBase structure is defined in exec/execbase.h. One of the
- fields in the ExecBase structure is MemList. It's a List structure that
- contains pointers to the list of memory regions which make up the memory
- free list. There is a MemList structure documented in the ROM Kernel Manual
- and in exec/memory.h. It is not related to the List structure named MemList
- in the ExecBase structure. The similar names could easily confuse you.
-
- Exec/lists.h defines a List structure as follows:
-
- struct List {
- struct Node *lh_Head;
- struct Node *lh_Tail;
- struct Node *lh_TailPred;
- UBYTE lh_Type;
- UBYTE l_pad;
- };
-
- The Node structure is defined in exec/nodes.h as follows:
-
- struct Node {
- struct Node *ln_Succ;
- struct Node *ln_Pred;
- UBYTE ln_Type;
- BYTE ln_Pri;
- char *ln_Name;
- };
-
- The memory free list is organized as a list of lists. MemList points
- to a list of memory regions. A region is a block of contiguous memory from
- which Exec can draw memory. Each region is described by a MemHeader
- structure. When the system is initialized it creates a region for chip RAM
- and one for each expansion RAM board in the system.
-
- If the expansion RAM boards occupy contiguous memory you can combine
- their regions into one with a program called MergeMem. MergeMem is in the
- System directory of the 1.3 Workbench disk.
-
- The MemHeader structure is documented in exec/memory.h as follows:
-
- struct MemHeader {
- struct Node mh_Node;
- UWORD mh_Attributes; /* characteristics of this region */
- struct MemChunk *mh_First; /* first free region */
- APTR mh_Lower; /* lower memory bound */
- APTR mh_Upper; /* upper memory bound+1 */
- ULONG mh_Free; /* total number of free bytes */
- };
-
- So the following gives the address of the first region in the memory
- free list:
-
- struct MemHeader *MemHeader;
- MemHeader = (struct MemHeader *)ExecBase->MemList.lh_Head;
-
- The MemHeader structure begins with a Node structure (mh_Node) which
- links this region to the others in the system's memory free list. Within
- the Node structure mh_Node.ln_Succ points to the next region and
- mh_Node.ln_Pred points to the preceding one. In the first node in the list
- mh_Node.ln_Pred points to the head of the list (MemHeader == MemHeader-
- >mh_Node.ln_Pred).
-
- The MemHeader structure defines the upper (mh_Upper) and lower limits
- (mh_Lower) of the region and tells how many bytes it currently has available
- (mh_Free). It contains a pointer to the first chunk of free memory
- (mh_First).
-
- Memory chunks are organized as a singly linked list. The NewChunk
- structure (documented in exec/memory.h) contains the address of the next
- chunk in mc_Next and the number of bytes in this chunk in mc_Bytes:
-
- struct MemChunk {
- struct MemChunk *mc_Next; /* pointer to next chunk */
- ULONG mc_Bytes; /* chunk byte size */
- };
-
- A NULL pointer in mc_Next marks the end of the list.
-
- Before any memory is allocated from a region there will be just one
- chunk in the list. The fields mh_First and mh_Lower will be equal and
- mh_Upper will be the same as mh_Free. The smallest amount of memory that
- can be allocated is eight bytes. That's because the smallest chunk of
- memory that can be replaced on the memory free list is eight bytes. A
- MemChunk requires four bytes for mc_Next (a 32-bit address) and four bytes
- for mc_Bytes (an unsigned long integer).
-
- When the first block of memory is allocated from a region the size of
- the block is added to mh_First and mh_Free is reduced by the size of the
- block. In the MemChunk structure mc_Next remains NULL and mc_Bytes is
- reduced by the amount of memory allocated.
-
- If a second block is allocated from the same region the size of the
- second block will be added to mh_First and subtracted from mh_Free and
- mc_Bytes. There will still be just one chunk in the list, although it will
- be smaller.
-
- The first fragmentation of the region occurs when the first block is
- freed while the second remains allocated. Then mh_First will be restored to
- its original value (equal to mh_Lower) and mh_Free will be increased by the
- size of the block. The address pointed to by mh_First (which is mc_Next)
- will be loaded with the previous value of mh_First. The size of the block
- will be loaded into mc_Bytes for the first MemChunk structure.
-
- Given Block1 as the size of the first block and Block2 as the size of
- the second block, the following relationships will be true after the first
- block is freed:
-
- mh_First == mh_Lower
- mh_Upper == mh_Free + Block2
- &mc_Next == mh_First
- mc_Next == &mc_Next + Block1 + Block2
- mc_Bytes == Block1
- mc_Next->mc_Next == NULL
- mc_Next->mc_Bytes == mh_Free - (Block1 + Block2)
-
- When the last block is freed mh_Free will equal the difference between
- mh_Upper and mh_Lower, mc_Next will be set to NULL, and mc_Bytes will be the
- same as mh_Free. In other words, the region will be restored to its
- original configuration with a single chunk that contains all the memory in
- the region.
-
- Chapter 6 of the Rom Kernel Manual: Exec describes the memory
- allocation routines Exec provides for programmers. There are three pairs of
- routines. AllocMem() and FreeMem() use the system's memory free list to
- manage one block per call. AllocEntry() and FreeEntry() process multiple
- blocks with a single call. Allocate() and Deallocate() allow you to manage
- blocks from a private memory free list.
-