mmapの使い方

概要

ファイルシステム上のファイルの特定の領域をメモリ上にマップして、メモリへの読み書きをそのままファイルに反映させる機能です。
構造体のメンバの更新などが、ファイル内のオフセットなどを気にすることなく出来るので、用途によっては非常に便利です。
通常通り、ファイルをopenして、そのファイルディスクリプタをmmapに指定します。
mmapの書式は次のようになっています。(詳細はman参照)

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

addrはメモリアドレスで、通常はNULLを与えれば適当な位置に割り当ててくれます。
lengthはマップするバイト数で任意のサイズが指定できます。 ここを、page sizeの倍数にしないといけないという人がいますが、これは誤りです。
Open Groupのドキュメントを見ると、次のようにあり、POSIXの規定ではマップの先頭からページの倍数の範囲で操作が行われるが、lengthに指定する値は、この制約を満たさなくても良い。つまりシステム側でケアする事になっています。

The system performs mapping operations over whole pages. Thus, while the parameter len need not meet a size or alignment constraint, the system shall include, in any mapping operation, any partial page specified by the address range starting at pa and continuing for len bytes.

lenthをファイル終端をはみ出す形で指定することも可能ですが、この場合は、はみ出した部分はファイルの更新が行われません。
protはパーミッションでfileのパーミッションに合わせておく必要があります。
flagsは、他のプロセスとマップを共有するかどうかをMAP_PRIVATE、MAP_SHAREDで指定しますが、「更新の通知はファイルを通じて行われる」==「ファイルの更新がされるかどうか」と解釈して良いようです。実際MAP_PRIVATEを指定すると、msyncしてもファイルの更新がされないので注意が必要です。
offsetはファイル上でのマップの開始位置です。これはpage sizeの倍数になっていなければならない制約があります。

ファイルへの反映は、プロセスが終了するか、munmapすれば行われますが、プロセス動作中に更新したい場合は、msyncを使用します。

サンプルプログラム

はじめに空のファイルを作り、その全体をmmapでマップしています。ファイル内のデータは文字配列として扱っており、無限ループで書き換えを行いながらmsyncでfileに反映させています。プログラムを動作させておいて、観察するとリアルタイムに書き換わっているのが確認できます。

一度mapしてしまえば、ファイルはcloseしてしまっても大丈夫です。

#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>

int main(void){
    const ssize_t size=40;

    /* Open stat file */
    int fd=open("out", O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
    if(fd < 0){
        perror("open");
        exit(EXIT_FAILURE);
    }
    /* create empty file */
    if(ftruncate(fd, size)){
        perror("ftruncate");
        exit(EXIT_FAILURE);
    }
    /* mmap the file */
    char *ptr=(char *)mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if(ptr == MAP_FAILED){
        perror("mmap");
        exit(EXIT_FAILURE);
    }
    close(fd);

    while(1){
        for(int i=0; i<size; i++){
            ptr[i] == 'z' ? ptr[i]='0' : ptr[i]++; 
            if(msync(ptr, size, MS_ASYNC))
                perror("msync");
        }
    }
    return(0);
}