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.

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.

댓글

이 블로그의 인기 게시물

토렌트: < 왕좌의 게임 > 시즌 1 ~ 시즌 8 완결편 마그넷

토렌트: NGC < 코스모스 > 우리말 더빙 전편(1편~13편) 마그넷

토렌트: < 스타워즈 > Ep.1 ~ Ep.6 마그넷