连续的mmap调用永远不会返回连续的地址

时间:2014-08-20 20:26:17

标签: c alignment mmap

函数page_allocate有效。它确实将地址返回到具有指定对齐的映射页面。但是,使用64k和1024k的连续呼叫永远不会连续。为什么呢?

  • ./ mmap 0x00001000 // 4k
  • ./ mmap 0x00002000 // 8k
  • ./ mmap 0x00004000 // 16k
  • ./ mmap 0x00008000 // 32k
  • ./ mmap 0x00010000 // 64k不好?连续分配中无法获得连续范围
  • ./ mmap 0x00020000 // 128k
  • ./ mmap 0x00040000 // 256k
  • ./ mmap 0x00080000 // 512k
  • ./ mmap 0x00100000 // 1024k bad?
  • ./ mmap 0x00200000 // 2048k
  • ./ mmap 0x00400000 // 4096k
  • ./ mmap 0x00800000 // 8192k
  • ./ mmap 0x01000000 // 16384k
  • ./ mmap 0x10000000 // 256Mb
  • ./ mmap 0x100000000 // 4096mb

这里包含一个示例程序。使用gcc -Wall -g mmap.c -o mmap进行编译。

#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;
}

1 个答案:

答案 0 :(得分:1)

您永远不应期望mmap自己提供连续的地址,但您可以尝试通过请求一个地址来获取它们,这将使新映射与地址一样长范围是免费的,并且省略MAP_FIXED以便所请求的地址仅用作提示(使用MAP_FIXED,它将替换已存在的地址,这绝对是不是你想要的。)

我怀疑你看到mmap为某些调用返回连续映射的原因,但不是全部,是内核试图平衡制作新VM区域的成本与希望拥有地址无法预测(ASLR)。