1

In LuaJIT on linux, all VM magaged ram has to be below the 2GB process memory boundary because the internal pointers are always 32bit. therefore I want to manage bigger allocs myself (using FFI and malloc,etc), e.g. for big textures, audio, buffers, etc.

I now want to make sure those are mapped above the 2GB boudary, because then they don't take any VM manageable memory away.

is there any way to malloc or maybe mmap(without mapped file, or maybe in SHM) to allocate a pointer specifically above that address? doesn't even have to take up the 2gig, just map my pointer to a higher (=non-32bit) address

nonchip
  • 1,084
  • 1
  • 18
  • 36

2 Answers2

4

Allocate a 2 GB block. If it is located below the limit, allocate another 2 GB (this one must be above 2 GB since only one block that size can fit below 2 GB).

/* Allocates size bytes at an address higher than address */
void *HighMalloc(size_t size, void *address) {
    size_t mysize = (size_t)address;
    void *y, *x;
    if (mysize < size) {
        mysize = size;
    }
    y = x = malloc(mysize);
    if (x < address) {
        /* The memory starts at a low address. 
         * mysize is so big that another block this size cannot fit 
         * at a low address. So let's allocate another block and 
         * then free the block that is using low memory. */
        x = malloc(mysize);
        free(y);
    }
    return x;
}

Note:

If size is smaller than address, there may be sufficient space at a low address at the second malloc. That is why I increase the allocated size in those cases. So don't use this to allocate small memory chunks. Allocate a big chunk and then divide it into smaller pieces manually.

Klas Lindbäck
  • 33,105
  • 5
  • 57
  • 82
  • You should malloc a big enough block that it is forced to be in the high RAM, then use your own allocator in there. – Antti Haapala -- Слава Україні Sep 16 '15 at 07:38
  • Could you explain how will it work exactly? Are you going to allocates >2GB chunks each time? I think `malloc` will start return NULL very soon... What do I miss here? – Alex Lop. Sep 16 '15 at 07:46
  • It wouldn't. For test I did allocate 64k 2 GB blocks. (Possibly even better *still* to use `mmap` for 2 GB, though it wouldn't be as portable.) – Antti Haapala -- Слава Україні Sep 16 '15 at 07:50
  • @AlexLop. I assume that the user will allocate a big block and then use his/her own allocator to get smaller pieces from that big block. – Klas Lindbäck Sep 16 '15 at 08:07
  • @KlasLindbäck Oh, I see. Thanks. Now it makes more sense. – Alex Lop. Sep 16 '15 at 08:17
  • @KlasLindbäck that's what I wanted to do anyway (actually using realloc to grow/shrink the high memory block as required, then just storing a struct into it. so I run your code with e.g. `(1GB,2GB)` and get a (at least) 1G block somewhere above 2G? (when calculating the correct pointers for those sizes of course) – nonchip Sep 16 '15 at 18:04
  • @nonchip If you do a `realloc` and you get a new address there is no guarantee that the new address will be above the 2 GB marker. – Klas Lindbäck Sep 17 '15 at 06:59
  • @KlasLindbäck so I should just manually "re-alloc" big chunks using your code, instead of `realloc`ing? – nonchip Sep 17 '15 at 08:36
  • 1
    Copying 2+ GB of memory is costly, so you don't want to do it unnecessarily. My gut feeling is that the probability of realloc moving the data to a low address is fairly low. So I would create a function `HighRealloc` that first tries a normal `realloc`, and only if the resulting pointer is too small calls `HighMalloc` and copies the data manually. – Klas Lindbäck Sep 17 '15 at 08:52
3

The only think that I know (so maybe it is not the best choice) is to use mmap for Linux. There were some situations when I had to allocate huge memory chunks aligned to specific values, so I used it (because here you can specify the address and the length of the memory chunk) but it requires to implement some memory manager unit since now you are going to manage the allocations (and releases).

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

Look here for details: http://man7.org/linux/man-pages/man2/mmap.2.html

To make it not mapped to any file just set flags to MAP_ANONYMOUS:

MAP_ANONYMOUS

The mapping is not backed by any file; its contents are initialized to zero. The fd and offset arguments are ignored; however, some implementations require fd to be -1 if MAP_ANONYMOUS (or MAP_ANON) is specified, and portable applications should ensure this. The use of MAP_ANONYMOUS in conjunction with MAP_SHARED is supported on Linux only since kernel 2.4.

If addr is NULL then the system will pick for you the available address but since you want to allocate it above the 2G you will need to manage a list of allocated pages in order to know which addresses are used above the 2G. Note also that if you specify that addr=X, and mmap will not be able to use this address it won't fail, it just will pick another address which can be used without any failure indication (except for the fact that the returned pointer will not be equal to addr). However you can use MAP_FIXED flag to enforce the address you supply and if mmap won't be able to use it, it will fail (return MAP_FAILED).

MAP_FIXED

Don't interpret addr as a hint: place the mapping at exactly that address. addr must be a multiple of the page size. If the memory region specified by addr and len overlaps pages of any existing mapping(s), then the overlapped part of the existing mapping(s) will be discarded. If the specified address cannot be used, mmap() will fail. Because requiring a fixed address for a mapping is less portable, the use of this option is discouraged.

EDIT

NOTE that using MAP_FIXED is not recommended since as the description says

If the memory region specified by addr and len overlaps pages of any existing mapping(s), then the overlapped part of the existing mapping(s) will be discarded.

and you will not even know about it. Safer to check that addr is equal to the returned by mmap address.

Alex Lop.
  • 6,810
  • 1
  • 26
  • 45