[Next] [Previous] | Chapter 2 |
In OS/2 1.3 the memory management scheme was designed to support
the Intel segmented architecture. The 80286 could provide access to
memory
in segments that were limited in size to 64K. At times more than 64K
was
necessary. In those cases, the developer would have to create elaborate
memory management schemes. This changed in OS/2 2.0. The amount of
memory
that developers can access is only limited by three items:
Thirty-two-bit OS/2 has only three segments that combine to make 4GB
total. This means that memory addresses are represented as a 0:32
pointer. All memory resides in these three segments. A normal program will run
in die segment that starts at address 0 and covers 480Mb. Protected
dynamic link libraries (DLLs see the same 480Mb region plus 32Mb above it 'This
512Mb addressability limitation is due to compatibility with 16-bit
OS/2 programs. The kernel functions see the full 4GB region. This is
where the big performance boost comes in. Because all memory is in these
three segments, when the operating system has to switch memory objects, the
segment registers do not always have to be loaded. A flat memory management
scheme has one more advantage: All pointers are near pointers, since all
memory can be addressed using a 0:32 pointer. This means no more 'FAR' jumps
for the operating system. This also means memory models -small. medium,
large, and huge -are now obsolete.
The basis of the 32-bit OS/2 memory management functions is DosAllocMem
. This function allocates memory in 4,096-byte chunks
called pages; however, a developer can allocate several contiguous
pages in one call. While this means that you can allocate any amount of
memory up to the process limit, it also means that you can waste a
considerable amount of memory if you're not careful.
Consider the following code fragment:
for (i=0; i< 1000; i++)
DosAllocMem(&p[i],
1,
PAG_READ | PAG_WRITE | PAG_COMMIT);
The first parameter is a PPVOID, the second parameter is the number of bytes allocated, and the last parameter is the memory flags. We'll see this again soon.
What you see in the code fragment is 1,000 1-byte blocks being allocated. What you don't see is the 1,000 4,095-byte blocks that are not being used because DosAllocMem allocates memory as an integral number of pages.
OS/2 2.0 also introduced the concept or committing memory. A call to DosAllocMem will reserve an address range for the memory; however, physical memory is actually assigned to the range only if the PAG_COMMIT flag is specified. (A side note here: In 32-bit OS/2, a page is only assigned to an address really when the page is touched.) If you try to access uncommitted memory, otherwise known as sparse memory objects TRAP-BOOM! If you choose to allocate memory without committing it, you have two ways of having it committed later - DosSetMem or DosSubSetMem. Also, in 32-bit OS/2, memory is guaranteed to be initialized to 0. This prevents the application from having to initialize the memory, thereby touching all the memory, thereby committing all the memory |
The following is a very simple program to allocate memory and to show a little about what happens to bad programs. Remember that we are seasoned professionals. Do not attempt this at home. Well, you may want to attempt it at home, but if you attempt this at work consistently, it may get you tired.
BADMEM.C
BADMEM.MAK
BADMEM.DEF
Now, you may look at this code and say, 'But, you're allocating only
3.000 bytes, and you're writing to 4,098.' Okay, this is bad code;
however, It illustrates that no matter how much you specify as bytes allocated,
the operating system will return it to you in 4,096 -byte pages, and
you could use them all and never see a protection violation. You'd just end
up stomping all over some data that you may need.
However, notice that when you try to write to byte 4097, TRAP ! This
too can happen to you, so be very careful about writing to unallocated,
uncommitted memory.
The flags used as the page attributes in the preceding example were
PAG_READ | PAG_WRITE | PAG_COMMIT. Table 2.1 lists the possible page
attributes.
Flag | Description |
PAG_READ | Read access is the only access allowed. A write to the memory location will generate a trap. |
PAG_WRITE | Read, write, and execute access is allowed. |
PAG_EXECUTE | Execute and read access to the memory is allowed. This flag sill also provide compatibility for future versions of the operating system. |
PAG_GUARD | Sets a guard page after the allocated memory object If any attempt is made to write to that guard page. a guard page fault exception is raised, and the application is given a chance to allocate more memory as needed. (See Chapter 6- Exception Handling) |
OBJ_TILE | All memory objects are put into the tiled, or compatibility, region in OS/2 2.x. All objects are aligned on 64K boundaries. Provides upward compatibility when applications will be allowed by future versions of the operating system to access regions above 512MB "16-bit compatibility" barrier |
Often the example programs and manuals will reference the default page attribute, fALLOC; this is a #define for OBJ_TILE | PAG_COMMIT | PAG_EXECUTE | PAG_READ | PAG_WRITE.
SUBMEM.C
SUBMEM.MAK
SUBMEM.DEF
You'll notice when you run this program that all your pointer sizes are rounded up In increments of 8 and that DosSubAllocMem starts allocating at the 65th byte of the memory object.
Gotcha!
All the processes involved with the shared memory (both the getting and giving) must free the shared memory before it is available foe reuse. If only one process frees the memory, you may begin to notice an increase in your program's memory consumption over time. The system maintains a usage count of shared memory that enables is to keep track of all she processes that have access to the shared memory. The IBM products THESEUS2 and SPM/2 are the only tools available to detect memory leakage. They are two excellent tools to monitor the system performance. |
The following programs are examples of allocating a named shared memory object. Notice that the memory is being allocated in a downward fashion; private memory is allocated upward from the bottom of the available space.
BATMAN.C
BATMAN.DEF
ROBIN.C
ROBIN.DEF
DYNDUO.H
DYNDUO.MAK
DosAllocMem, DosSubSetMem, and DosSubAllocMem might
seem like a bit of overkill if you would like to have only 20 bytes for
a string every now and then. And they are. These functions are moss
useful for large programs that allocate large quantities of memory at one
time,allocate shared memory, or have special memory needs. For most smaller
applications, malloc from an ANSI C compiler will
be just fine.
Also, you probably will find that malloc is much more portable to other
versions of OS/2 running on top of the Power PC. The C Set++ version of
malloc is the only compiler version of malloc that
will be compared to DosAllocMem and company.
In most cases malloc will provide memory to the program
just as fast as DosAllocMem. The C Set++ compiler uses a special algorithm,
designed to provide the expected amount of memory in the fastest time.
The following program uses mailer to allocate memory and then displays
the amount of memory allocated plus the location of the
pointer in memory. You probably will start to notice a pattern emerging, and
there is one.
SPEED.C
SPEED.MAK
SPEED.DEF
By looking at the program's output, you'll notice that memory allocation starts by using 32 for values between 1 and 16. It uses 64 for values between 17 and 32, 128, 256, and finally 512. You may notice a few "bumps" in the algorithm. They occur when the C runtime is using some of the memory for its own purposes.
[Next] [Previous] | Chapter 2 |