OS/2 codes: How to allocate in high-memory area
By default, OS/2 allocates a memory in low-memory area, which is below 512 MB. However, nowadays, more and more applications requires more and more memory. Fortunately, OS/2 provides ways to allocate in high-memory area, which is above 512 MB. In this case, applications can use much more memory limited by VIRTUALADDRESSLIMIT and physical memory + swap memory. So if possible, it would be better to try to allocate in high-memory area first.
Now, let's see the ways to allocate in a high-memory.
1. DosAllocMem() with OBJ_ANY
First is to use OS/2 API DosAllocMem(). DosAllocMem() provides a flag to allocate in high-memory area. It's OBJ_ANY. Following is an example.
DosAllocMem() and OBJ_ANY can be used regardless of compilers. However, DosAllocMem() itself allocates memory in page size, which is 4K. In addition, DosAllocMem() on OS/2 Warp allocates in 64K without access rights. Therefore, if you allocate 1 byte with DosAllocMem(), 64K is allocated practically. Memory except 1 byte has no access rights. So if you want to use DosAllocMem() directly, it would be better to implement your own memory allocator from a memory pool allocated by DosAllocMem() to mange a memory efficiently, or use malloc() family of your libc.
2. _hmalloc()/_hcalloc()/_hrealloc()
kLIBC provides functions to allocate in high-memory area. They are _hmalloc(), _hcalloc() and _hrealloc(). As you may know from their name, they are high-memory version of malloc() family. Additionally, kLIBC provides _hstrdup(), which is a high-memory version of strdup().
To use them, you should include emx/umalloc.h first. See the example.
However, when porting programs and libraries using malloc() family extensively, it's annoying to replace malloc() family to _hmalloc() family. For this, you can use -Zhigh-mem linker flag.
3. malloc()/calloc()/realloc() with -Zhigh-mem
If you use -Zhigh-mem linker flag, you can use malloc() family as-is. See the example.
You can confirm the difference of value of p when linking with or without -Zhigh-mem flag. That is, to enable high-memory support, use a following command
By the way, in spite of using -Zhigh-mem, high-memory usage may not be enabled. For example, if you link against high-memory-not-enabled DLLs, then -Zhigh-mem flag is ignored. This is for the safety. If high-memory objects are passed to those DLLs, it may crash or mis-behave in those DLLs. Because some OS/2 APIs does not support high-memory objects.
Nevertheless, there is a time when high-memory support is needed. In this case, override malloc() family with _hmalloc() family.
4. Overriding malloc()/calloc()/realloc()
kLIBC has a unique feature to override standard functions. Here, use it. For details, see 'Alias' part of OS/2 codes: How to override standard functions in kLIBC.
Following is the example.
In this example, malloc() try to allocate in high-memory area first. If not possbiel, try to allocate in low-memory area. This assures to allocate in high-memory area if possible regardless of -Zhigh-mem flag. And note that this is applied globally.
5. Passing high-memory objects to functions not supporting high-memory objects
As I said above, passing high-memory objects to functions without high-memory support, may cause crahses. Then how can we avoid this problem ?
You can use a buffer in a stack or allocated by _lmalloc(), which is a low-memory version of malloc(). That is, copy the contents of a high-memory object to the buffer, and pass the buffer instead. Or allocate a memory to pass to high-memory-support-not-enabled functions with _lmalloc() at the beginning.
Now, let's see the ways to allocate in a high-memory.
1. DosAllocMem() with OBJ_ANY
First is to use OS/2 API DosAllocMem(). DosAllocMem() provides a flag to allocate in high-memory area. It's OBJ_ANY. Following is an example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /* highmem1.c */ #define INCL_DOS #include <os2.h> #include <stdio.h> int main( void ) { PVOID p; APIRET rc; rc = DosAllocMem( &p, 1024, fPERM | PAG_COMMIT | OBJ_ANY ); printf("rc = %lu, p = %p, 512 MB = %p\n", rc, p, 512 * 1024 * 1024L ); DosFreeMem( p ); return 0; } |
DosAllocMem() and OBJ_ANY can be used regardless of compilers. However, DosAllocMem() itself allocates memory in page size, which is 4K. In addition, DosAllocMem() on OS/2 Warp allocates in 64K without access rights. Therefore, if you allocate 1 byte with DosAllocMem(), 64K is allocated practically. Memory except 1 byte has no access rights. So if you want to use DosAllocMem() directly, it would be better to implement your own memory allocator from a memory pool allocated by DosAllocMem() to mange a memory efficiently, or use malloc() family of your libc.
2. _hmalloc()/_hcalloc()/_hrealloc()
kLIBC provides functions to allocate in high-memory area. They are _hmalloc(), _hcalloc() and _hrealloc(). As you may know from their name, they are high-memory version of malloc() family. Additionally, kLIBC provides _hstrdup(), which is a high-memory version of strdup().
To use them, you should include emx/umalloc.h first. See the example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* highmem2.c */ #include <stdio.h> #include <emx/umalloc.h> int main( void ) { void *p; p = _hmalloc( 1024 ); printf("p = %p, 512 MB = %p\n", p, 512L * 1024 * 1024 ); free( p ); return 0; } |
However, when porting programs and libraries using malloc() family extensively, it's annoying to replace malloc() family to _hmalloc() family. For this, you can use -Zhigh-mem linker flag.
3. malloc()/calloc()/realloc() with -Zhigh-mem
If you use -Zhigh-mem linker flag, you can use malloc() family as-is. See the example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* highmem3.c */ #include <stdio.h> #include <stdlib.h> int main( void ) { void *p; p = malloc( 1024 ); printf("p = %p, 512 MB = %p\n", p, 512L * 1024 * 1024 ); free( p ); return 0; } |
You can confirm the difference of value of p when linking with or without -Zhigh-mem flag. That is, to enable high-memory support, use a following command
gcc -Zhigh-mem highmem3.c
By the way, in spite of using -Zhigh-mem, high-memory usage may not be enabled. For example, if you link against high-memory-not-enabled DLLs, then -Zhigh-mem flag is ignored. This is for the safety. If high-memory objects are passed to those DLLs, it may crash or mis-behave in those DLLs. Because some OS/2 APIs does not support high-memory objects.
Nevertheless, there is a time when high-memory support is needed. In this case, override malloc() family with _hmalloc() family.
4. Overriding malloc()/calloc()/realloc()
kLIBC has a unique feature to override standard functions. Here, use it. For details, see 'Alias' part of OS/2 codes: How to override standard functions in kLIBC.
Following is the example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /* highmem4.c */ #include <stdio.h> #include <stdlib.h> #include <emx/umalloc.h> int main( void ) { void *p; p = malloc( 1024 ); printf("p = %p, 512 MB = %p\n", p, 512L * 1024 * 1024 ); free( p ); return 0; } void *malloc( size_t size ) { void *p; p = _hmalloc( size ); if( !p ) p = _lmalloc( size ); return p; } |
In this example, malloc() try to allocate in high-memory area first. If not possbiel, try to allocate in low-memory area. This assures to allocate in high-memory area if possible regardless of -Zhigh-mem flag. And note that this is applied globally.
5. Passing high-memory objects to functions not supporting high-memory objects
As I said above, passing high-memory objects to functions without high-memory support, may cause crahses. Then how can we avoid this problem ?
You can use a buffer in a stack or allocated by _lmalloc(), which is a low-memory version of malloc(). That is, copy the contents of a high-memory object to the buffer, and pass the buffer instead. Or allocate a memory to pass to high-memory-support-not-enabled functions with _lmalloc() at the beginning.
댓글
댓글 쓰기