In short I want to resize memory but have the old memory in the middle of the new.
So what I did was use mmap
for the initial size (p1
), mmap
at an address before p1
to pretend I made the memory bigger, then treat the new pointer as if I created it with a single mmap
(p3
, mremap
). The code seems to work but I'm not sure if this is what I should be doing. If it isn't how should I create more memory and have the old/current memory be in the middle of it?
#include <sys/mman.h>
#include <cstdio>
#include <cstring>
#include <cassert>
#include <cerrno>
int main()
{
auto p1 = (char*) mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (!p1 || p1==(char*)-1)
return -1;
auto p1_32 = (int *)p1;
memset(p1, 0, 4096);
p1_32[5] = 1035;
auto p2 = mmap(p1-4096, 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
if (!p2 || p2==(void*)-1)
return -2;
auto p2_32 = (int *)p2;
memset(p2, 0, 4096);
p2_32[5] = 5301;
assert(p2_32[1024+5] == 1035);
auto p3 = mremap(p2, 4096*2, 4096*4, MREMAP_MAYMOVE);
if (p3==(char*)-1)
{
printf("Errno %d\n", errno);
return -2;
}
auto p3_32 = (int*)p3;
assert(p3_32[5] == 5301);
assert(p3_32[1024+5] == 1035);
printf("Is this correct?\n");
return 0;
}
As described here
The
munmap()
function shall remove any mappings for those entire pages containing any part of the address space of the process starting ataddr
and continuing forlen
bytes.
so it is allowed to remove multiple mappings with a single munmap
call (as if it was a single mapping).
There is a problem with your code though: how do you know if a page (p2
) before your page (p1
) is not used? It could be already allocated by other pars of the program (including malloc
), by using MAP_FIXED
like this you will rewrite (remap) its content:
When
MAP_FIXED
is set in the flags argument, the implementation is informed that the value of pa shall beaddr
, exactly. IfMAP_FIXED
is set,mmap()
may returnMAP_FAILED
and seterrno
to[EINVAL]
. If aMAP_FIXED
request is successful, the mapping established bymmap()
replaces any previous mappings for the process' pages in the range[pa,pa+len)
.
So I don't think this trick can be useful in the general case, you should use mremap
instead.
As for how this is implemented: Linux does merge sequential private anonymous mappings, so both will be merged into a single vma_struct
in the kernel. This "feature" has undesirable side effects such as munmap
failing to free memory with ENOMEM
. But this is more of an implementation detail, not something you have control over.