メモリマッピング

mmap()システムコールは、ファイルまたはデバイスをメモリにマップする呼び出しプロセスの仮想アドレス空間でのマッピングを提供します。これには2つのタイプがあります-

File mapping or File-backed mapping−このマッピングは、プロセスの仮想メモリの領域をファイルにマップします。これは、メモリのこれらの領域への読み取りまたは書き込みにより、ファイルの読み取りまたは書き込みが行われることを意味します。これはデフォルトのマッピングタイプです。

Anonymous mapping−このマッピングは、ファイルに裏打ちされていないプロセスの仮想メモリの領域をマップします。内容はゼロに初期化されます。このマッピングは、動的メモリ割り当て(malloc())に似ており、特定の割り当てのために一部のmalloc()実装で使用されます。

1つのプロセスマッピングのメモリは、他のプロセスのマッピングと共有される場合があります。これは2つの方法で行うことができます-

  • 2つのプロセスがファイルの同じ領域をマップする場合、それらは物理メモリの同じページを共有します。

  • 子プロセスが作成されると、そのプロセスは親のマッピングを継承し、これらのマッピングは親と同じ物理メモリのページを参照します。子プロセスでデータが変更されると、子プロセス用に異なるページが作成されます。

2つ以上のプロセスが同じページを共有している場合、各プロセスは、マッピングタイプに応じて、他のプロセスによって行われたページコンテンツの変更を確認できます。マッピングタイプはプライベートまたは共有のいずれかです-

Private Mapping (MAP_PRIVATE) −このマッピングの内容への変更は他のプロセスには表示されず、マッピングは基になるファイルに反映されません。

Shared Mapping (MAP_SHARED) −このマッピングの内容への変更は他のプロセスに表示され、マッピングは基になるファイルに実行されます。

#include <sys/mman.h>

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

上記のシステムコールは、成功した場合はマッピングの開始アドレスを返し、エラーの場合はMAP_FAILEDを返します。

仮想アドレスaddrは、ユーザーが指定するか、カーネルによって生成されます(addrをNULLとして渡す場合)。示されているフィールド長には、バイト単位のマッピングのサイズが必要です。フィールドprotは、PROT_NONE、PROT_READ、PROT_WRITE、PROT_EXECなど、それぞれアクセス、読み取り、書き込み、または実行できない領域を対象としたメモリ保護値を示します。この値は、単一(PROT_NONE)にすることも、3つのフラグ(最後の3つ)のいずれかとORdにすることもできます。フィールドフラグは、マッピングタイプまたはMAP_PRIVATEまたはMAP_SHAREDのいずれかを示します。フィールド「fd」は、マップされるファイルを識別するファイル記述子を示し、フィールド「offset」はファイルの開始点を示します。ファイル全体をマップする必要がある場合は、オフセットをゼロにする必要があります。

#include <sys/mman.h>

int munmap(void *addr, size_t length);

上記のシステムコールは、成功した場合は0を返し、エラーの場合は-1を返します。

システムコールmunmapは、すでにメモリマップされている領域のマッピング解除を実行します。フィールドaddrはマッピングの開始アドレスを示し、長さはマッピング解除されるマッピングのバイト単位のサイズを示します。通常、マッピングとマッピング解除は、マッピングされた領域全体に対して行われます。これを変える必要がある場合は、縮小するか、2つの部分にカットする必要があります。addrにマッピングがない場合、この呼び出しは効果がなく、呼び出しは0(成功)を返します。

例を考えてみましょう-

Step 1 −以下に示すように、ファイルの英数字を書き込みます−

0 1 2 25 26 27 28 29 30 31 32 33 34 35 36 37 38 59 60 61
A B C Z 0 1 2 3 4 5 6 7 8 9 A b c x y z

Step 2− mmap()システムコールを使用して、ファイルの内容をメモリにマップします。これにより、メモリにマップされた後に開始アドレスが返されます。

Step 3−高価なread()システムコールを読み取らないため、配列表記を使用してファイルの内容にアクセスします(ポインター表記を使用してアクセスすることもできます)。メモリマッピングを使用して、ユーザースペース、カーネルスペースバッファー、およびバッファーキャッシュ間の複数のコピーを回避します。

Step 4 −ユーザーが「-1」(アクセスの終了を意味する)を入力するまで、ファイルの内容を繰り返し読み取ります。

Step 5 −クリーンアップアクティビティを実行します。つまり、マップされたメモリ領域のマッピングを解除し(munmap())、ファイルを閉じて、ファイルを削除します。

/* Filename: mmap_test.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
void write_mmap_sample_data();

int main() {
   struct stat mmapstat;
   char *data;
   int minbyteindex;
   int maxbyteindex;
   int offset;
   int fd;
   int unmapstatus;
   write_mmap_sample_data();
   if (stat("MMAP_DATA.txt", &mmapstat) == -1) {
      perror("stat failure");
      return 1;
   }
   
   if ((fd = open("MMAP_DATA.txt", O_RDONLY)) == -1) {
      perror("open failure");
      return 1;
   }
   data = mmap((caddr_t)0, mmapstat.st_size, PROT_READ, MAP_SHARED, fd, 0);
   
   if (data == (caddr_t)(-1)) {
      perror("mmap failure");
      return 1;
   }
   minbyteindex = 0;
   maxbyteindex = mmapstat.st_size - 1;
   
   do {
      printf("Enter -1 to quit or ");
      printf("enter a number between %d and %d: ", minbyteindex, maxbyteindex);
      scanf("%d",&offset);
      if ( (offset >= 0) && (offset <= maxbyteindex) )
      printf("Received char at %d is %c\n", offset, data[offset]);
      else if (offset != -1)
      printf("Received invalid index %d\n", offset);
   } while (offset != -1);
   unmapstatus = munmap(data, mmapstat.st_size);
   
   if (unmapstatus == -1) {
      perror("munmap failure");
      return 1;
   }
   close(fd);
   system("rm -f MMAP_DATA.txt");
   return 0;
}

void write_mmap_sample_data() {
   int fd;
   char ch;
   struct stat textfilestat;
   fd = open("MMAP_DATA.txt", O_CREAT|O_TRUNC|O_WRONLY, 0666);
   if (fd == -1) {
      perror("File open error ");
      return;
   }
   // Write A to Z
   ch = 'A';
   
   while (ch <= 'Z') {
      write(fd, &ch, sizeof(ch));
      ch++;
   }
   // Write 0 to 9
   ch = '0';
   
   while (ch <= '9') {
      write(fd, &ch, sizeof(ch));
      ch++;
   }
   // Write a to z
   ch = 'a';
   
   while (ch <= 'z') {
      write(fd, &ch, sizeof(ch));
      ch++;
   }
   close(fd);
   return;
}

出力

Enter -1 to quit or enter a number between 0 and 61: 3 
Received char at 3 is D 
Enter -1 to quit or enter a number between 0 and 61: 28
Received char at 28 is 2 
Enter -1 to quit or enter a number between 0 and 61: 38 
Received char at 38 is c 
Enter -1 to quit or enter a number between 0 and 61: 59 
Received char at 59 is x 
Enter -1 to quit or enter a number between 0 and 61: 65 
Received invalid index 65 
Enter -1 to quit or enter a number between 0 and 61: -99 
Received invalid index -99 
Enter -1 to quit or enter a number between 0 and 61: -1