본문 바로가기

Programming/Linux_Platform

mmap (user space)의 MAP_ANONYMOUS 에 대해서...

 

mmap은 보통 file이나 kernel이 alloc한 memory를 user space에서 remap해서 VA로 접근하기 위해 사용하지만,

MAP_ANONYMOUS 로 사용할 경우는, addr=0, fd=-1로 설정하게 된다.

어떤 case에서 이러한 mapping을 사용하는지 보자.

 

우선, MAP_ANONYMOUS로 할당한다고 해도, user application 상에서 이 memory를 read write 하는데는 아무런 문제가 없다는 것을 미리 알아두자.

 


 

 

http://jake.dothome.co.kr/user-virtual-maps-mmap2/

 

어떠한 파일하고도 연결되지 않고 0으로 초기화된 영역.

fd는 무시되지만 어떤 경우에는 -1이 요구된다.

offset는 0으로 한다.

MAP_SHARED와 같이 사용될 수도 있다. (커널 v2.4)

 


 

https://stackoverflow.com/questions/34042915/what-is-the-purpose-of-map-anonymous-flag-in-mmap-system-call

 

MAP_ANONYMOUS + MAP_PRIVATE:

  • every call creates a distinct mapping 

       (각 call 마다 별개의 mapping이 생성된다.)

  • children inherit parent's mappings 

       (child process들은 부모의 mapping을 상속 받음)

  • childrens' writes on the inherited mapping are catered in copy-on-write manner 

       (자식 process가 상속받은 mapping영역에 write를 할때, CoW(별개의 영역으로 기존 내용이 복사되고, 수정이 적용됨) 방식으로 동작함.

  • the main purpose of using this kind of mapping is to allocate a new zeroized memory 

       (이러한 종류의 mapping을 사용하는 주 목적은 0으로 초기화된 memory를 할당하기 위해서 임)

  • malloc employs anonymous private mappings to serve memory allocation requests larger than MMAP_THRESHOLD bytes.
    typically, MMAP_THRESHOLD is 128kB. 

       (malloc은 MMAP_THRESHOLD(보통 128KB)보다 큰 memory allocation을 하기 위해서, anonymous private mapping을 사용한다.)

 

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

 

 

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

       VA에 fragmentation이 없다. unmapping후에 memory는 즉시 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

 

int main(int argc, char *argv[])

{

    /*Pointer to shared memory region*/    

    int *addr;   

 

#ifdef USE_MAP_ANON      /*Use MAP_ANONYMOUS*/           

     addr = mmap(NULLsizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -10);     

     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(NULLsizeof(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);

}