記憶體對映



mmap() 系統呼叫在呼叫程序的虛擬地址空間中提供對映,將檔案或裝置對映到記憶體。它分為兩種型別:

檔案對映或檔案支援對映 - 此對映將程序虛擬記憶體的區域對映到檔案。這意味著對這些記憶體區域進行讀寫操作會導致檔案被讀寫。這是預設的對映型別。

匿名對映 - 此對映將程序虛擬記憶體的區域對映到記憶體,但不依賴於任何檔案。內容初始化為零。這種對映類似於動態記憶體分配 (malloc()),並在某些 malloc() 實現中用於某些分配。

一個程序的記憶體對映可以與其他程序的對映共享。這可以透過兩種方式完成:

  • 當兩個程序對映檔案的同一區域時,它們共享相同的物理記憶體頁。

  • 如果建立子程序,它將繼承父程序的對映,這些對映引用與父程序相同的物理記憶體頁。在子程序中對資料進行任何更改後,將為子程序建立不同的頁面。

當兩個或多個程序共享相同的頁面時,每個程序都可以看到其他程序對頁面內容所做的更改,具體取決於對映型別。對映型別可以是私有的或共享的:

私有對映 (MAP_PRIVATE) - 對此對映內容的修改對其他程序不可見,並且對映不會傳遞到底層檔案。

共享對映 (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 時)。length 欄位指示對映的大小(以位元組為單位)。prot 欄位指示記憶體保護值,例如 PROT_NONE、PROT_READ、PROT_WRITE、PROT_EXEC,分別表示不允許訪問、讀取、寫入或執行的區域。此值可以是單個值 (PROT_NONE),也可以與任何三個標誌(最後三個)進行 OR 運算。flags 欄位指示對映型別,即 MAP_PRIVATE 或 MAP_SHARED。fd 欄位指示標識要對映的檔案的檔案描述符,offset 欄位表示檔案的起始點,如果需要對映整個檔案,則 offset 應為零。

#include <sys/mman.h>

int munmap(void *addr, size_t length);

上述系統呼叫成功時返回 0,出錯時返回 -1。

系統呼叫 munmap 執行已對映記憶體區域的取消對映。addr 欄位指示對映的起始地址,length 欄位指示要取消對映的對映的大小(以位元組為單位)。通常,對映和取消對映將針對整個對映區域。如果必須不同,則應縮小或分成兩部分。如果 addr 沒有任何對映,則此呼叫不會產生任何影響,並且呼叫返回 0(成功)。

讓我們考慮一個例子:

步驟 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

步驟 2 - 使用 mmap() 系統呼叫將檔案內容對映到記憶體。這將返回對映到記憶體後的起始地址。

步驟 3 - 使用陣列表示法訪問檔案內容(也可以使用指標表示法訪問),因為它不會讀取昂貴的 read() 系統呼叫。使用記憶體對映,避免使用者空間、核心空間緩衝區和緩衝區快取之間多次複製。

步驟 4 - 重複讀取檔案內容,直到使用者輸入“-1”(表示訪問結束)。

步驟 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
廣告
© . All rights reserved.