What is the purpose of MAP_ANONYMOUS flag in mmap system call?

前端 未结 1 855
说谎
说谎 2020-12-04 22:01

From the man page,

MAP_ANONYMOUS
              The mapping is not backed by any file; its contents are initialized to zero.  The fd and offset a         


        
相关标签:
1条回答
  • 2020-12-04 22:31

    Anonymous mappings can be pictured as a zeroized virtual file. Anonymous mappings are simply large, zero-filled blocks of memory ready for use. These mappings reside outside of the heap, thus do not contribute to data segment fragmentation.

    MAP_ANONYMOUS + MAP_PRIVATE:

    • every call creates a distinct mapping
    • children inherit parent's mappings
    • childrens' writes on the inherited mapping are catered in copy-on-write manner
    • the main purpose of using this kind of mapping is to allocate a new zeroized memory
    • malloc employs anonymous private mappings to serve memory allocation requests larger than MMAP_THRESHOLD bytes.
      typically, MMAP_THRESHOLD is 128kB.

    MAP_ANONYMOUS + MAP_SHARED:

    • each call creates a distinct mapping that doesn't share pages with any other mapping
    • children inherit parent's mappings
    • no copy-on-write when someone else sharing the mapping writes on the shared mapping
    • shared anonymous mappings allow IPC in a manner similar to System V memory segments, but only between related processes

    On Linux, there are two ways to create anonymous mappings:

    • specify MAP_ANONYMOUS flag and pass -1 for fd

          addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); 
          if (addr == MAP_FAILED)
              exit(EXIT_FAILURE);  
      
    • open /dev/zero and pass this opened fd

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

      (this method is typically used on systems like BSD, that do not have MAP_ANONYMOUS flag)

    Advantages of anonymous mappings:
    - no virtual address space fragmentation; after unmapping, the memory is immediately returned to the system
    - they are modifiable in terms of allocation size, permissions and they can also receive advice just like normal mappings
    - each allocation is a distinct mapping, separate from global heap

    Disadvantages of anonymous mappings:
    - size of each mapping is an integer multiple of system's page size, thus it can lead to wastage of address space
    - creating and returning mappings incur more overhead than that of from the pre-allocated heap

    if a program containing such mapping, forks a process, the child inherits the mapping. The following program demonstrates this kinda inheritance:

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

    Sources:
    The Linux Programming Interface
    Chapter 49: Memory Mappings,
    Author: Michael Kerrisk

    Linux System Programming (3rd edition)
    Chapter 8: Memory Management,
    Author: Robert Love

    0 讨论(0)
提交回复
热议问题