共有ストレージマッピング mmap/munmap

概要:

メモリ マップド I/O は、ディスク ファイルを記憶領域のバッファにマップします。

        したがって、バッファからデータをフェッチするときは、ファイル内の対応するバイトを読み取ることと同じです。これと同様に、データがバッファに格納されると、対応するバイトが自動的にファイルに書き込まれます。このようにして、読み取りおよび書き込み関数を使用せずに、アドレス (ポインター) を使用して I/O 操作を完了できます。

        共有メモリは、プロセスがデータをコピーせずにメモリを直接読み書きできるため、最も便利なプロセス間通信方法であり、IPC の最速の形式であると言えます。

ストレージマッピング機能

関数プロトタイプ:

<sys/mman.h>
void *mmap(void *addr,size_t length,int prot,int flags,int fd,off_t offset);
int munmap(void *addr,size_t length);

(1)mmap関数

メモリマッピングの手順:

  • open システムコールでファイルを開き、ファイル記述子 fd を返します。
  • mmap を使用してメモリ マッピングを作成し、マッピングの最初のアドレス ポインタ addr を返します。
  • マッピング(ファイル)に対する各種操作・表示・変更を行う
  • munmap(void *addr,size_t length) を使用してメモリ マッピングを閉じます
  • close システムコールを使用してファイル fd を閉じます。

void *mmap(void *adrr, size_t 長さ, int prot, int flags, int fd, off_t offset);

パラメータ:    

         addr: マッピング領域の先頭アドレスを指定します。通常、システムが自動的に割り当てることを示すために NULL が渡されます。

         length: 作成される共有メモリマッピング領域のサイズ (<= ファイルの実際のサイズ)

         prot: マッピング領域の権限 PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE

         flags: フラグパラメータ (物理領域の設定と更新、共有の設定、匿名マッピング領域の作成に一般的に使用されます)

                                  MAP_SHARED: マップされた領域で実行された操作が物理デバイス (ディスク) に反映されます。

                                  MAP_PRIVATE: マッピング領域に加えられた変更は物理デバイスには反映されません。

         fd: 共有メモリマッピング領域の作成に使用されるファイル記述子

         offset: デフォルトは 0 で、すべてのファイルがマップされることを意味します。マッピング ファイルのオフセット (4k の整数倍)

戻る:

        成功: 作成されたマッピング領域の最初のアドレスを返します。

        失敗: MAP_FAILED(-1) を返します。エラーの理由は errno に格納されます。

関数には主に 3 つの用途があります。

  • ファイルの頻繁な読み書きが必要な場合、通常のファイルをメモリにマッピングすることで、I/O の読み書きをメモリの読み書きに置き換え、より高いパフォーマンスを実現します。

  • 特殊ファイルの匿名メモリ マッピングにより、関連するプロセスに共有メモリ領域を提供できます。

  • 無関係なプロセスに共有メモリ空間を提供し、通常は通常のファイルをメモリにマッピングします

考える:

1. 新しいファイルを開くときに、マッピング領域を作成するために新しいファイルを O_CREAT できますか?

2. open 中に O_RDONLY が指定され、mmap 中に PROT パラメータとして PROT_READ|PROT_WRITE が指定された場合はどうなりますか?

3. ファイル記述子が最初に閉じられた場合、mmap マッピングに影響はありますか?

4. ファイルのオフセットが 1000 の場合はどうなりますか?

5. mem が境界を越えるとどうなりますか?

6. mem++ の場合、munmap は成功しますか?

7. mmap 呼び出しはどのような状況で失敗しますか?

8. mmap の戻り値が検出されなかった場合はどうなりますか?

概要: mmap を使用するときは、次の点に注意してください。

  1. マッピング領域を作成するプロセスには、マッピング ファイルの読み取り操作が含まれます。
  2. MAP_SHARED の場合、要件: マッピング領域の権限は、ファイルを開く権限以下である必要があります (マッピング領域の保護のため)。mmap の権限はメモリ制限であるため、MAP_PRIVATE は問題ではありません。
  3. マップされた領域の解放は、ファイルのクローズとは関係がありません。マッピングが正常に確立されている限り、ファイルはすぐに閉じることができます。
  4. なお、マッピングファイルサイズが0の場合、マッピング領域は作成できません。つまり、マッピングに使用されるファイルは実際のサイズでなければなりません。mmap を使用すると、バス エラーがよく発生します。これは、通常、共有ファイル ストレージ領域のサイズが原因です。たとえば、マッピング領域の作成時にオフセットが 4096 バイトの 400 バイトのファイルの場合、バス エラーが報告されます。
  5. munmap によって渡されるアドレスは、mmap の戻りアドレスである必要があります。pointer++ 操作は絶対に避けてください。
  6. ファイルのオフセットが 4K の整数倍である必要がある場合
  7. mmap がマッピング領域を作成するときにエラーが発生する可能性が非常に高いため、後続の操作を続行する前に必ず戻り値をチェックして、マッピング領域が正常に作成されたことを確認してください。

(2) ムンマップ関数

メモリ空間に適用する malloc 関数と同様に、mmap で作成したマッピング領域も使用後に free と同様の関数を呼び出して解放する必要があります。

int munmap(void *addr, size_t 長さ); 

        成功: 0、失敗: -1

ケース 1: mmap がマッピング領域を作成する

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<error.h>
#include<pthread.h>
#include<sys/mman.h>
#include<fcntl.h>

void sys_err(const char *str)
{
    perror(str);
    exit(1);
}

int main(int argc, char *argv[])
{
    char *p = NULL;
    int fd;

    fd = open("testmap", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if(fd == -1)
        sys_err("open error");

    //lseek(fd ,10 , SEEK_END);  //两个函数等同于 ftruncate()函数
    //write(fd, "\0", 1);

    ftruncate(fd, 20); //需要写权限,才能拓展文件大小
    int len = lseek(fd, 0, SEEK_END);

    p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(p == MAP_FAILED){
        sys_err("mmap error");
    }

    //使用 p 对文件进行读写操作
    strcpy(p, "hello mmap");  //写操作
    
    printf("-----%s\n", p);

    int ret = munmap(p, len);
    if(ret == -1){
        sys_err("munmap error");
    }

    return 0;
}

ケース 2: mmap の親子プロセス通信

演習:親プロセスはマッピング領域を作成し、子プロセスをフォークし、子プロセスはマッピング領域の内容を変更します。その後、親プロセスはマッピング領域の内容を読み取り、共有されているかどうかを確認します。

結論: 親プロセスと子プロセスは以下を共有します: 1.ファイルを開きます。 2. mmap によって作成されたマッピング領域 (ただし、MAP_SHARED を使用する必要があります)

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/mman.h>
#include<sys/wait.h>
#include<fcntl.h>

int var = 100;

int main(int argc, char *argv[])
{
    int *p;
    pid_t pid;

    int fd;
    fd = open("temp", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if(fd < 0){
        perror("open error");
        exit(1);
    }
    ftruncate(fd, 4); //需要写权限,才能拓展文件大小

    p = (int *)mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    //p = (int *)mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
    if(p == MAP_FAILED){  //注意:不是 p == NULL
        perror("open error");
        exit(1);
    }
    close(fd);  //映射区建立完毕,即可关闭文件
    
    pid = fork();  //创建子进程
    if(pid == 0){
        *p = 2000;  //写共享内存
        var = 1000;
        printf("child, *p = %d, var = %d\n", *p, var);
    } else {
        sleep(1);
        printf("parent, *p = %d, var = %d\n", *p, var);  //读共享内存
        wait(NULL);
 
        int ret = munmap(p, 4);  //释放映射区
        if(ret == -1){
            perror("open error");
            exit(1);
        }
    }

    return 0;
}

ケース 3: mmap に関係のないプロセス通信

本質的に、mmap はファイルを使用してマッピング領域の作成を支援するカーネルであり、複数のプロセスがこのマッピング領域を使用してデータ転送を完了します。カーネル空間は複数のプロセスで共有されるため、mmap を使用して無関係なプロセス間の通信を完了することもできます。対応するフラグパラメータ flags を設定するだけです。共有を実現したい場合は、もちろん MAP_SHARED を使用する必要があります。

MAP_ANON も /dev/zero も、血に関係のないプロセス間通信には適用できないことに注意してください。親子プロセス間でのみ使用できます。

//mmap_w.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<error.h>
#include<sys/mman.h>
#include<fcntl.h>

struct student{
    int id;
    char name[256];
    int age;
};

void sys_err(const char *str)
{
    perror(str);
    exit(1);
}

int main(int argc, char *argv[])
{
    struct student stu = {1, "xiaoming", 18};
    struct student *p;
    int fd;

    //fd = open("test_map", O_RDWR);
    fd = open("test_map", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if(fd == -1){
        sys_err("open error");
    }

    ftruncate(fd, sizeof(stu)); //需要写权限,才能拓展文件大小

    p = mmap(NULL, sizeof(stu), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(p == MAP_FAILED){
        sys_err("mmap error");
    }

    close(fd);

    while(1){
        memcpy(p, &stu, sizeof(stu));
        stu.id++;
        sleep(1);
    }
    
    munmap(p, sizeof(stu));

    return 0;
}
//mmap_r.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<error.h>
#include<sys/mman.h>
#include<fcntl.h>

struct student{
    int id;
    char name[256];
    int age;
};

void sys_err(const char *str)
{
    perror(str);
    exit(1);
}

int main(int argc, char *argv[])
{
    struct student stu;
    struct student *p;
    int fd;

    fd = open("test_map", O_RDONLY);
    if(fd == -1){
        sys_err("open error");
    }

    p = mmap(NULL, sizeof(stu), PROT_READ, MAP_SHARED, fd, 0);
    if(p == MAP_FAILED){
        sys_err("mmap error");
    }

    close(fd);

    while(1){
        printf("id= %d, name= %s, age=%d\n", p->id, p->name, p->age);
        sleep(1);
    }
    
    munmap(p, sizeof(stu));

    return 0;
}

 

おすすめ

転載: blog.csdn.net/weixin_43200943/article/details/129797841