Memory Management
The Bass Demon
Fri 11. September
1998
Introduction
This article will shed some light on various ways of keeping track of memory. Since I'm not totally "into" this stuff myself, I will use this text to "experiment" with various techniques. First I'll start by describing ways of keeping track of the 1st MB of memory in the range 00000000-000FFFFF. Then I will try to implement a scheme of keeping track of memory in the 1MB-4GB range.
Note that this page is very colorful, this because this topic is rather gray and boring...
1st MB of Memory
When talking about the 1st MB of memory, I'm talking about real-mode, a segmented memory model, with no 32-bit addressing and memory paging.
Certain areas of low memory are reserved for specific purposes and this must be handled in the proper way. In order to explain this I will show you a sample memory map of the 1st MB of memory:
00000 |
003FF |
IVT |
Interrupt Vector Table | |||
00400 |
004FF |
BDA |
BIOS Data Area. | |||
00500 |
00501 |
PRTSCR |
1st byte is Print Screen Status BYTE. | |||
00501 |
9xxxx |
OS |
OS specific. | |||
07C00 |
07CFF |
BOOT |
Where Boot Sectors are loaded by the BIOS. | |||
9xxxx |
9FFFF |
EBDA |
Extended BIOS Data Area, varies in size (at least 1KB). | |||
A0000 |
AFFFF |
VIDEO |
Used by the Video Adapter (Graphics Mode). | |||
B0000 |
B7FFF |
VIDEO |
Used by the Video Adapter (Monochrome). | |||
B8000 |
BFFFF |
VIDEO |
Used by the Video Adapter (Textmode). | |||
C0000 |
C7FFF |
VIDEO |
Used by the Video BIOS. | |||
C8000 |
EFFFF |
ROM |
May be used by adapter ROMs or as memory mapped I/O. | |||
F0000 |
FFFFF |
BIOS |
System BIOS, 32K (starts at F8000) or 64K. |
The memory map will vary from system to system, but most likely it will look similar to the one above. Probably the most varying memory is in the range C0000-FFFFF.
The size of the EBDA, if it is present, will be specified in kilobytes by a WORD at offset 13H within the BIOS Data Area. The OS can use all memory up to the point specified here, but must provide a method of sharing this memory with programs it wishes to load, otherwise the system will be useless. There are some other programs that might use this memory, such as viruses and other resident software loaded before the OS. Either via a ROM or by the boot sector.
Here are some methods of keeping track of this memory:
Method 1:
Keep a pointer similar to the one at BDA[13] somewhere. Initially set it to point to the segment above the last space you need for your OS' kernel and resident drivers. Then whenever you need to load a new program/driver/module/whatever, you just increase this pointer with the amount of memory you need. You then give the program the previous and current values of this pointer so it knows which range it can use.
Method 2:
Split the memory range into blocks as needed, and keep a small structure at the start of each blocks with information about the status of the block. This is the DOS method of memory management. The advantage of this is that you can free up memory in any order. The disadvantage is memory fragmentation and a bit of complexity to newbies.
Method 3a:
Store a bitmap somewhere in memory with each bit representing the status of a chunk of memory. You can choose between "big chunks and a small bitmap" or "small chunks and a big bitmap". A good choice would be 1KB chunks. With this size the bitmap would use 640 bits, or 80 bytes. When memory is needed just find a range which is large enough. Obviously using a chunk size of 16 bytes would be more flexible, but it would give you a larger bitmap - 5KB or 5120 bytes. This method requires the OS or each program to "remember" what memory ranges it uses, so it would properly free it up when not needed anymore.
Method 3b:
Similar to the method above, but with 2 bits for each memory chunk. With 2 bits per chunk, you could indicate more than just used/unused. You could for instance use the following values:
0 - Free (simply unused, available memory)
1 - System
Memory (OS and drivers).
2 - Program Memory (Code and Data for application
programs).
3 - Reserved Memory (Can be used to specify ROMs and adapter
memory).
Method 4:
This method is an extension of method 3a or 3b. In addition to a bitmap, let the OS keep a small table with starting/ending memory chunk pairs or similar. Whenever a program needs memory, just check this table for an unused pair (zero).
As you can see, it's only your imagination that can stop you from designing your memory manager.
With many memory managers you are given a "handle" when you successfully allocate memory, and pass this "handle" when you deallocate memory. This "handle" can be the starting address of a memory area/block (method 1 or 2 - DOS method). For methods 3a/3b the program itself must store this information. The "handle" is often used as an index into a table of memory info (suitable for method 4).
If you decided to go with the bitmap/handle method you could let the bitmap cover the entire 1MB memory range and initially just set the bits representing the IVT, BDA, OS, VIDEO and ROM areas. This would stop the memory manager from using these ranges.
That's about all that can be said about memory management for real-mode. Simple stuff. Let's then move on to memory management for memory above 1MB, with 386+ protected mode.
1MB-4GB Memory Area
As you probably have noticed, I've beautifully skipped the odd 286, with it's semi-implementation of protected mode. The reason for this is that the 286 uses the same 64K segments as the 8086, but includes the selector scheme of the 386 which allows you to specify a 24-bit base address for segment selectors. The most useful memory management technique for this processor would be the bitmap/handle scheme I mentioned earlier, though with a slightly bigger handle table. Enough about the 286 processor...
With the 386 you have a special mode where you can use a function called "paging". With paging enabled the processor puts and extra layer between the logical linear address and the physical address placed on the memory bus during MemR/MemW cycles. The processor divides the memory space into 4KB chunks called simply pages. Newer Pentiums can also enable the use of 2MB/4MB pages, but I will ignore this feature right now, because I'm not very familiar with it.
With paging the system can have one or more "Page Directories" each containing pointers to "Page Tables" which in turn points to a single page of 4KB. The address of the page directory page, is specified in Control Register 3, if you want to know more about this, get one of the Intel manuals.
What method can be used to keep track of memory in this mode?
If you forget about paging and think you do not need or want to implement virtual memory, you can use any of the methods used in my real-mode section. The method of keeping a single "last used address" pointer, is a very stupid and cheap solution. Also there are some problem with this solution called "memory holes" and "memory mapped I/O". Most BIOS's have an option to enable a so-called memory hole at 15MB-16MB for use with adapters (most likely PCI). Also most video cards today support a "linear frame buffer".
This leads us to the bitmap/handle method, once again. If you keep a bitmap with one or two bits for each 4KB page, you can initially simply set the bits where memory holes, adapters, etc. resides. Then you will need a big table of some kind to store the address pairs used when keeping track of allocated memory. OK, here are the methods I can think of:
Method 1:
Keep a bitmap with one bit for each 4KB page. If you have 128MB of memory, the memory will be divided into 32768 pages, each using one bit in the bitmap. The bitmap will then neatly fit into one page! In addition to this include a table with several 2 DWORD address pairs describing a starting/ending memory address. All the bits covered by the range will then be set in the bitmap, and cleared when memory chunk is deallocated.
Method 2:
Same as above, but with two bits per page. This allows a more detailed status information about memory to be stored. As I suggest above, these three values could be used:
0 - Free
1 - System Memory
2 - Program Memory
3
- Reserved Memory
Method 3:
Keep our well known bitmap, any version, 1-bit or 2-bit version, but drop the handle table. Instead use the global descriptor table to keep track of which memory ranges belongs to which application. Use the base address as starting page, and the descriptor limit as count of pages. When a program needs to allocate memory, it will get a descriptor value, similar to a memory handle.
There's really not much more to say about memory
management,
have fun, and write a great OS!!