📜  内存映射

📅  最后修改于: 2021-01-18 06:45:55             🧑  作者: Mango


mmap()系统调用提供了将文件或设备映射到内存的调用过程的虚拟地址空间中的映射。这有两种类型-

文件映射或文件支持的映射-此映射将进程的虚拟内存区域映射到文件。这意味着读取或写入那些内存区域会导致文件被读取或写入。这是默认的映射类型。

匿名映射-此映射映射进程的虚拟内存区域,没有任何文件支持。内容初始化为零。此映射类似于动态内存分配(malloc()),并在某些malloc()实现中用于某些分配。

一个进程映射中的内存可以与其他进程中的映射共享。这可以通过两种方式完成-

  • 当两个进程映射文件的相同区域时,它们共享相同的物理内存页面。

  • 如果创建了子进程,则该子进程将继承父进程的映射,并且这些映射引用与父进程相同的物理内存页面。一旦子进程中的数据发生任何更改,将为子进程创建不同的页面。

当两个或多个进程共享同一页面时,每个进程可以看到其他进程根据映射类型所做的页面内容更改。映射类型可以是私有的或共享的-

专用映射(MAP_PRIVATE) -对此映射内容的修改对其他进程不可见,并且该映射也不携带到基础文件。

共享映射(MAP_SHARED) -对此映射内容的修改对其他进程可见,并且映射被携带到基础文件。

#include 

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个)进行ORd。字段标志指示映射类型为MAP_PRIVATE或MAP_SHARED。字段“ fd”表示标识要映射文件的文件描述符,字段“偏移”表示文件的起点,如果需要映射整个文件,则偏移应为零。

#include 

int munmap(void *addr, size_t length);

上面的系统调用成功返回0或错误返回-1。

系统调用munmap执行已内存映射区域的取消映射。字段addr指示映射的起始地址,长度指示要映射的映射的字节大小。通常,映射和取消映射将针对整个映射区域。如果必须不同,则应将其缩小或切成两部分。如果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 
#include 
#include 
#include 
#include 
#include 
#include 
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