web-dev-qa-db-ja.com

mmapシステムコールのMAP_ANONYMOUSフラグの目的は何ですか?

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でマップされたメモリを他のプロセスと共有するにはどうすればよいですか?

21
GeekyJ

匿名マッピングは、ゼロ化された仮想ファイルとして表すことができます。匿名マッピングは、使用可能なメモリの単純に大きく、ゼロで満たされたブロックです。これらのマッピングはヒープの外にあるため、データセグメントの断片化には寄与しません。

MAP_ANONYMOUS + MAP_PRIVATE:

  • 呼び出しごとに異なるマッピングが作成されます
  • 子は親のマッピングを継承します
  • 継承されたマッピングでの子供の書き込みは、コピーオンライト方式で提供されます
  • この種類のマッピングを使用する主な目的は、新しいゼロ化メモリを割り当てることです
  • mallocは、匿名のプライベートマッピングを使用して、MMAP_THRESHOLDバイトより大きいメモリ割り当て要求に対応します。
    通常、MMAP_THRESHOLDは128kBです。

MAP_ANONYMOUS + MAP_SHARED:

  • 各呼び出しは、他のマッピングとページを共有しない別個のマッピングを作成します
  • 子は親のマッピングを継承します
  • no copy-on-writeマッピングを共有している他の誰かが共有マッピングに書き込む場合
  • 共有匿名マッピングは、System Vのメモリセグメントと同様の方法でIPC=を許可しますが、関連するプロセス間でのみ可能です。

Linuxでは、匿名マッピングを作成する方法が2つあります。

  • 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章:メモリ管理、
著者:ロバートラブ

34
nachiketkulk