41

From the man page,

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.

What is the purpose of using MAP_ANONYMOUS? Any example would be good. Also From where the memory will be mapped?

It is written on man page that The use of MAP_ANONYMOUS in conjunction with MAP_SHARED is only supported on Linux since kernel 2.4. How can i share the memory mapped with MAP_ANONYMOUS with other process?

Jagdish
  • 1,848
  • 3
  • 22
  • 33

1 Answers1

71

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

nachiketkulk
  • 1,141
  • 11
  • 22
  • 2
    hi @nachiketkulk, Does MAP_ANONYMOUS means contiguous virtual memory ? – ransh Apr 02 '17 at 14:45
  • 10
    @ransh: `mmap` always creates a single contiguous mapping of virtual addresses so you can use it as an array. The underlying physical pages don't have to be contiguous or in any particular order, as usual with virtual memory. – Peter Cordes May 10 '18 at 22:08
  • `mmap` can merge new mappings with compatible mappings with the result that `munmap` returns ENOMEM if unmapping would result in the limit of mapping being exceeded. – Timothy Baldwin Jul 06 '20 at 15:42
  • In the example program, I cannot see any reason to `munmap` it twice(both in child and parent process). – karlbsm May 12 '21 at 12:58
  • @PeterCordes *"These mappings reside outside of the heap, thus do not contribute to data segment fragmentation."* But how does heap contribute to data segment fragmentation? In Linux these are two different segments.. – NK-cell Jun 08 '21 at 14:25
  • 2
    @NK-cell: "The heap" isn't a real thing anyway, so that phrase in the answer isn't very clear. Perhaps it's talking about space allocated with `brk`? (Which can only be safely handed back to the OS by moving the break to a lower address if *all* intervening allocations have been freed). So sure, using brk (like a reverse stack doing LIFO allocations) means fragmentation of those allocations can be a problem (large free-list you can't give back to the OS), and the break is normally at the end of the data segment (i.e. after the BSS). – Peter Cordes Jun 08 '21 at 14:39
  • 1
    This answer's terminology in the opening paragraph is only helpful for people that already understand the details of what actually happens, if that's what it was even intended to mean. But yes, being able to give the memory back to the OS right away on `free` is why glibc `malloc` uses `mmap` for large allocations, even on systems where it uses brk for small allocations. – Peter Cordes Jun 08 '21 at 14:39
  • 1
    @PeterCordes So knowing that the glibc's `malloc()` has such a fork (`brk` for small allocations vs `mmap` for the large ones) we can just do `malloc()` assuming that there is nothing more to optimize here? I.e. knowing this we don't need to use anon `mmap` explicitly? – NK-cell Jun 08 '21 at 15:44
  • @NK-cell: glibc malloc is not the fastest implementation, I think. Some replacement libraries are commonly used. And it's always possible for it to do poorly with huge amounts of small allocations. Usually it's good enough, and if not you should look for another existing malloc implementation, though. I guess it could make sense to manually use mmap in a specific place if you always want page alignment, and you always still know the size when you're going to free (so you don't need any extra bookkeeping metadata), and the allocation is always large and used for a long time. – Peter Cordes Jun 08 '21 at 21:58
  • If you just need to reserve some RAM, `malloc()` is the correct way to proceed. Specialized allocation routine is needed if `malloc()` performance is not good enough (and with modern Linux libc it's pretty fast already!) and you want anonymous `mmap()` in case you need to share RAM segment between processes or need explicit point in code to free the memory back to OS. Note that you have to touch every page in anonymous `mmap` to force real allocation of RAM, otherwise it's just virtual memory address space segment not backed up by actual RAM. – Mikko Rantalainen Aug 16 '22 at 09:34