【问题标题】:What is the purpose of MAP_ANONYMOUS flag in mmap system call?mmap 系统调用中 MAP_ANONYMOUS 标志的目的是什么?
【发布时间】:2016-03-06 16:54:34
【问题描述】:

来自man 页面,

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 only supported on Linux since kernel 2.4.

使用MAP_ANONYMOUS的目的是什么?任何例子都会很好。还要从哪里映射内存?

写在man页面上The use of MAP_ANONYMOUS in conjunction with MAP_SHARED is only supported on Linux since kernel 2.4. 如何与其他进程共享使用 MAP_ANONYMOUS 映射的内存?

【问题讨论】:

    标签: linux memory memory-management system-calls mmap


    【解决方案1】:

    匿名映射可以被描述为一个零化的虚拟文件。 匿名映射只是可供使用的大型、零填充的内存块。 这些映射位于堆之外,因此不会造成数据段碎片。

    MAP_ANONYMOUS + MAP_PRIVATE:

    • 每次调用都会创建不同的映射
    • 子代继承父代的映射
    • 孩子对继承映射的写入以写时复制的方式提供服务
    • 使用这种映射的主要目的是分配一个新的归零内存
    • malloc 使用匿名私有映射来处理大于 MMAP_THRESHOLD 字节的内存分配请求。
      通常,MMAP_THRESHOLD 为 128kB。

    MAP_ANONYMOUS + MAP_SHARED:

    • 每次调用都会创建一个不与任何其他映射共享页面的不同映射
    • 子代继承父代的映射
    • no copy-on-write 当共享映射的其他人在共享映射上写入时
    • 共享匿名映射允许 IPC 以类似于 System V 内存段的方式进行,但仅限于相关进程之间

    在 Linux 上,有两种方法可以创建匿名映射:

    • 指定 MAP_ANONYMOUS 标志并为 fd 传递 -1

          addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); 
          if (addr == MAP_FAILED)
              exit(EXIT_FAILURE);  
      
    • 打开 /dev/zero 并传递这个打开的 fd

          fd = open("/dev/zero", O_RDWR);   
          addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
      

      (这种方法通常用在像 BSD 这样没有 MAP_ANONYMOUS 标志的系统上)

    匿名映射的优点:
    - 没有虚拟地址空间碎片;解映射后立即将内存返回给系统
    - 它们在分配大小、权限方面是可修改的,它们也可以像普通映射一样接收建议
    - 每个分配都是一个不同的映射,独立于全局堆

    匿名映射的缺点:
    - 每个映射的大小是系统页面大小的整数倍,因此会导致地址空间的浪费
    - 创建和返回映射比从预分配的堆产生更多的开销

    如果包含此类映射的程序派生一个进程,则子进程将继承该映射。 下面的程序演示了这种继承:

    #ifdef USE_MAP_ANON
    #define _BSD_SOURCE
    #endif  
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <sys/wait.h>
    #include <sys/mman.h>
    #include <fcntl.h>
    #include <unistd.h>
    
    int main(int argc, char *argv[])
    {
        /*Pointer to shared memory region*/    
        int *addr;   
    
    #ifdef USE_MAP_ANON      /*Use MAP_ANONYMOUS*/           
         addr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);     
         if (addr == MAP_FAILED) {     
             fprintf(stderr, "mmap() failed\n");     
             exit(EXIT_FAILURE);
         }      
    
    #else        /*Map /dev/zero*/     
        int fd;    
        fd = open("/dev/zero", O_RDWR);      
        if (fd == -1) {    
            fprintf(stderr, "open() failed\n");
            exit(EXIT_FAILURE);
        }    
    
        addr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);    
        if (addr == MAP_FAILED) {    
            fprintf(stderr, "mmap() failed\n");    
            exit(EXIT_FAILURE);    
        }     
    
        if (close(fd) == -1) {          /*No longer needed*/    
            fprintf(stderr, "close() failed\n");    
            exit(EXIT_FAILURE);    
        }
    #endif    
        *addr = 1;      /*Initialize integer in mapped region*/    
    
        switch(fork()) {        /*Parent and child share mapping*/     
        case -1:    
            fprintf(stderr, "fork() failed\n");
            exit(EXIT_FAILURE);    
    
        case 0:         /*Child: increment shared integer and exit*/     
            printf("Child started, value = %d\n", *addr);    
            (*addr)++;    
    
            if (munmap(addr, sizeof(int)) == -1) {    
                fprintf(stderr, "munmap()() failed\n");    
                exit(EXIT_FAILURE);    
            }     
            exit(EXIT_SUCCESS);     
    
        default:        /*Parent: wait for child to terminate*/      
            if (wait(NULL) == -1) {    
                fprintf(stderr, "wait() failed\n");    
                exit(EXIT_FAILURE);      
            }     
    
            printf("In parent, value = %d\n", *addr);         
            if (munmap(addr, sizeof(int)) == -1) {       
                fprintf(stderr, "munmap()() failed\n");      
                exit(EXIT_FAILURE);       
            }        
            exit(EXIT_SUCCESS);
    }
    

    来源:
    Linux 编程接口
    第49章:内存映射,
    作者:迈克尔·克里斯克

    Linux 系统编程(第 3 版)
    第 8 章:内存管理,
    作者:罗伯特·洛夫

    【讨论】:

    • 嗨@nachiketkulk,MAP_ANONYMOUS 是否意味着连续的虚拟内存?
    • @ransh: mmap 总是创建一个虚拟地址的连续映射,因此您可以将其用作数组。底层物理页面不必像通常的虚拟内存那样连续或按任何特定顺序排列。
    • mmap 可以将新映射与兼容映射合并,如果取消映射会导致超出映射限制,munmap 会返回 ENOMEM。
    • @NK-cell:“堆”无论如何都不是真实的东西,所以答案中的那个短语不是很清楚。也许它正在谈论分配给brk 的空间? (如果 所有 干预分配已被释放,则只能通过将中断移动到较低地址来安全地将其交还给操作系统)。所以可以肯定的是,使用 brk(如反向堆栈执行 LIFO 分配)意味着这些分配的碎片可能是一个问题(您无法将大型空闲列表返回给操作系统),并且中断通常在数据的末尾段(即在 BSS 之后)。
    • 这个答案在开头段落中的术语只对已经了解实际发生的细节的人有帮助,如果这就是它的意思的话。但是,是的,能够在 free 上立即将内存还给操作系统,这就是为什么 glibc malloc 使用 mmap 进行大分配,即使在使用 brk 进行小分配的系统上也是如此。
    猜你喜欢
    • 2019-07-10
    • 2021-01-28
    • 2020-12-05
    • 1970-01-01
    • 1970-01-01
    • 2011-09-25
    • 2017-06-03
    • 2017-08-13
    • 1970-01-01
    相关资源
    最近更新 更多