The function page_allocate
work. It does return address to mapped pages with the specified alignment. However consecutive call using 64k and 1024k are never contiguous. Why?
Here is included a sample program. Use gcc -Wall -g mmap.c -o mmap
to compile.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#define _GNU_SOURCE /* MAP_ANONYMOUS */
#include <sys/mman.h>
#include <unistd.h> /* sysconf */
static size_t sys_page_size = 0;
void
page_init(void)
{
int sc;
if(!sys_page_size)
{
sc = sysconf(_SC_PAGESIZE);
if(sc == -1)
{
sys_page_size = 0x0000000000001000; /* Default to 4Kb */
}
else
{
sys_page_size = sc;
}
}
}
void *
page_allocate(size_t request,
size_t alignment)
{
size_t size;
size_t slop;
void *addr;
/* Round up to page size multiple.
*/
request = (request + (sys_page_size - 1)) & ~(sys_page_size -1);
alignment = (alignment + (sys_page_size - 1)) & ~(sys_page_size -1);
size = request + alignment;
/* Maybe we get lucky.
*/
addr = mmap(NULL,
request,
PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS,
-1,
0);
if(!((uintptr_t)addr & (alignment - 1)))
{
return addr;
}
munmap(addr, request);
addr = mmap(NULL,
size,
PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS,
-1,
0);
slop = (uintptr_t)addr & (request - 1);
#define POINTER_OFFSET(addr, offset) ((void *)((uintptr_t)(addr) + (offset)))
if(slop)
{
munmap(addr, request - slop);
munmap(POINTER_OFFSET(addr, size - slop), slop);
addr = POINTER_OFFSET(addr, request - slop);
}
else
{
munmap(POINTER_OFFSET(addr, request), request);
}
return addr;
}
int
main(int argc,
char **argv)
{
size_t size;
void *cmp = NULL;
void *ptr[16];
int i;
page_init();
if(argc == 2)
{
size = strtol(argv[1], NULL, 16);
}
else
{
size = 0x00001000;
}
for(i = 0;
i < 16;
i ++)
{
ptr[i] = page_allocate(size, size);
}
for(i = 0;
i < 16;
i ++)
{
printf("%2i: %p-%p %s\n",
i,
ptr[i],
(void *)((uintptr_t)ptr[i] + size - 1),
(llabs(ptr[i] - cmp) == size) ? "contiguous" : "");
cmp = ptr[i];
}
return 1;
}
You should never expect mmap
to provide contiguous addresses on its own, but you could attempt to get them by requesting an address that would make the new mapping contiguous as long as the address range is free, and omitting MAP_FIXED
so that the requested address is only used as a hint (with MAP_FIXED
, it would replace what's already there, which is definitely not what you want).
I suspect the reason you're seeing mmap
return contiguous maps for some calls, but not all, is that the kernel is trying to balance the cost of making new VM areas with the desire to have addresses be unpredictable (ASLR).
User contributions licensed under CC BY-SA 3.0