Отображение памяти

Системный вызов mmap () обеспечивает отображение в виртуальном адресном пространстве вызывающего процесса, который отображает файлы или устройства в память. Это два типа -

File mapping or File-backed mapping- Это отображение отображает область виртуальной памяти процесса на файлы. Это означает, что чтение или запись в эти области памяти приводит к чтению или записи файла. Это тип сопоставления по умолчанию.

Anonymous mapping- Это отображение отображает область виртуальной памяти процесса без поддержки какого-либо файла. Содержимое инициализируется нулем. Это сопоставление похоже на распределение динамической памяти (malloc ()) и используется в некоторых реализациях malloc () для определенных распределений.

Память в одном отображении процесса может использоваться совместно с отображениями в других процессах. Это можно сделать двумя способами -

  • Когда два процесса отображают одну и ту же область файла, они используют одни и те же страницы физической памяти.

  • Если создается дочерний процесс, он наследует сопоставления родителя, и эти сопоставления относятся к тем же страницам физической памяти, что и родительский. При любом изменении данных в дочернем процессе для дочернего процесса будут созданы разные страницы.

Когда два или более процессов совместно используют одни и те же страницы, каждый процесс может видеть изменения содержимого страницы, сделанные другими процессами, в зависимости от типа сопоставления. Тип сопоставления может быть частным или общим -

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). Флаги поля указывают тип отображения либо MAP_PRIVATE, либо MAP_SHARED. Поле «fd» указывает дескриптор файла, идентифицирующий файл, который нужно сопоставить, а поле «смещение» подразумевает начальную точку файла, если необходимо сопоставить весь файл, смещение должно быть нулевым.

#include <sys/mman.h>

int munmap(void *addr, size_t length);

Вышеупомянутый системный вызов возвращает 0 в случае успеха или -1 в случае ошибки.

Системный вызов munmap выполняет отключение уже отображенной области памяти. Поля addr указывают начальный адрес отображения, а длина указывает размер в байтах отображения, которое необходимо отключить. Обычно отображение и отключение выполняется для всех отображаемых регионов. Если он должен быть другим, то его следует либо усадить, либо разрезать на две части. Если у 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