Mapeamento de Memória

A chamada de sistema mmap () fornece mapeamento no espaço de endereço virtual do processo de chamada que mapeia os arquivos ou dispositivos na memória. Isso é de dois tipos -

File mapping or File-backed mapping- Este mapeamento mapeia a área da memória virtual do processo para os arquivos. Isso significa que ler ou gravar nessas áreas da memória faz com que o arquivo seja lido ou gravado. Este é o tipo de mapeamento padrão.

Anonymous mapping- Este mapeamento mapeia a área da memória virtual do processo sem o apoio de nenhum arquivo. O conteúdo é inicializado em zero. Esse mapeamento é semelhante à alocação de memória dinâmica (malloc ()) e é usado em algumas implementações de malloc () para certas alocações.

A memória em um mapeamento de processo pode ser compartilhada com mapeamentos em outros processos. Isso pode ser feito de duas maneiras -

  • Quando dois processos mapeiam a mesma região de um arquivo, eles compartilham as mesmas páginas de memória física.

  • Se um processo filho é criado, ele herda os mapeamentos do pai e esses mapeamentos referem-se às mesmas páginas da memória física do pai. Após qualquer alteração de dados no processo filho, páginas diferentes seriam criadas para o processo filho.

Quando dois ou mais processos compartilham as mesmas páginas, cada processo pode ver as alterações do conteúdo da página feitas por outros processos, dependendo do tipo de mapeamento. O tipo de mapeamento pode ser privado ou compartilhado -

Private Mapping (MAP_PRIVATE) - As modificações no conteúdo deste mapeamento não são visíveis para outros processos e o mapeamento não é transportado para o arquivo subjacente.

Shared Mapping (MAP_SHARED) - As modificações no conteúdo deste mapeamento são visíveis para outros processos e o mapeamento é transportado para o arquivo subjacente.

#include <sys/mman.h>

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

A chamada de sistema acima retorna o endereço inicial do mapeamento em caso de sucesso ou MAP_FAILED em erro.

O endereço virtual addr pode ser especificado pelo usuário ou gerado pelo kernel (ao passar addr como NULL). O comprimento do campo indicado requer o tamanho do mapeamento em bytes. O campo prot indica valores de proteção de memória como PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC destinados a regiões que não podem ser acessadas, lidas, escritas ou executadas respectivamente. Este valor pode ser único (PROT_NONE) ou pode ser ORd com qualquer um dos três sinalizadores (últimos 3). Os sinalizadores de campo indicam o tipo de mapeamento ou MAP_PRIVATE ou MAP_SHARED. O campo 'fd' indica o descritor de arquivo que identifica o arquivo a ser mapeado e o campo 'offset' indica o ponto de partida do arquivo, caso seja necessário mapear todo o arquivo, o offset deve ser zero.

#include <sys/mman.h>

int munmap(void *addr, size_t length);

A chamada de sistema acima retorna 0 em caso de sucesso ou -1 em caso de erro.

A chamada do sistema munmap, executa o desmapeamento da região já mapeada na memória. Os campos addr indicam o endereço inicial do mapeamento e o comprimento indica o tamanho em bytes do mapeamento a ser desmapeado. Normalmente, o mapeamento e desmapeamento seria para todas as regiões mapeadas. Se tiver que ser diferente, deve ser encolhido ou cortado em duas partes. Se o addr não tiver nenhum mapeamento, esta chamada não terá efeito e a chamada retornará 0 (sucesso).

Vamos considerar um exemplo -

Step 1 - Grave em arquivo Alfa caracteres numéricos conforme mostrado abaixo -

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- Mapeie o conteúdo do arquivo na memória usando a chamada de sistema mmap (). Isso retornaria o endereço inicial após mapeado na memória.

Step 3- Acesse o conteúdo do arquivo usando a notação de array (também pode acessar com a notação de ponteiro), pois não lê uma chamada de sistema read () cara. Usando o mapeamento de memória, evite cópias múltiplas entre o espaço do usuário, os buffers do espaço do kernel e o cache do buffer.

Step 4 - Repita a leitura do conteúdo do arquivo até que o usuário digite “-1” (significa fim de acesso).

Step 5 - Execute atividades de limpeza, ou seja, desmapeando a região de memória mapeada (munmap ()), fechando o arquivo e removendo o arquivo.

/* 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;
}

Resultado

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