Mapeo de memoria

La llamada al sistema mmap () proporciona un mapeo en el espacio de direcciones virtuales del proceso de llamada que mapea los archivos o dispositivos en la memoria. Esto es de dos tipos:

File mapping or File-backed mapping- Este mapeo asigna el área de la memoria virtual del proceso a los archivos. Esto significa que leer o escribir en esas áreas de la memoria hace que el archivo se lea o escriba. Este es el tipo de mapeo predeterminado.

Anonymous mapping- Este mapeo mapea el área de la memoria virtual del proceso sin respaldo de ningún archivo. El contenido se inicializa a cero. Este mapeo es similar a la asignación de memoria dinámica (malloc ()) y se usa en algunas implementaciones de malloc () para ciertas asignaciones.

La memoria en un mapeo de proceso puede compartirse con mapeos en otros procesos. Esto se puede hacer de dos formas:

  • Cuando dos procesos mapean la misma región de un archivo, comparten las mismas páginas de memoria física.

  • Si se crea un proceso hijo, hereda las asignaciones del padre y estas asignaciones se refieren a las mismas páginas de la memoria física que la del padre. Ante cualquier cambio de datos en el proceso hijo, se crearían diferentes páginas para el proceso hijo.

Cuando dos o más procesos comparten las mismas páginas, cada proceso puede ver los cambios en el contenido de la página realizados por otros procesos dependiendo del tipo de mapeo. El tipo de mapeo puede ser privado o compartido:

Private Mapping (MAP_PRIVATE) - Las modificaciones al contenido de este mapeo no son visibles para otros procesos y el mapeo no se lleva al archivo subyacente.

Shared Mapping (MAP_SHARED) - Las modificaciones al contenido de este mapeo son visibles para otros procesos y el mapeo se lleva al archivo subyacente.

#include <sys/mman.h>

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

La llamada al sistema anterior devuelve la dirección de inicio de la asignación en caso de éxito o MAP_FAILED en caso de error.

La dirección virtual addr, puede ser especificada por el usuario o generada por el kernel (al pasar la dirección como NULL). La longitud del campo indicada requiere el tamaño de la asignación en bytes. El campo prot indica valores de protección de memoria como PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC destinados a regiones a las que no se puede acceder, leer, escribir o ejecutar respectivamente. Este valor puede ser único (PROT_NONE) o puede ser ORd con cualquiera de las tres banderas (las últimas 3). Las banderas de campo indican el tipo de mapeo o MAP_PRIVATE o MAP_SHARED. El campo 'fd' indica el descriptor de archivo que identifica el archivo a ser mapeado y el campo 'desplazamiento' implica el punto de inicio del archivo, si necesita mapear el archivo completo, el desplazamiento debe ser cero.

#include <sys/mman.h>

int munmap(void *addr, size_t length);

La llamada al sistema anterior devuelve 0 en caso de éxito o -1 en caso de error.

La llamada al sistema munmap, realiza el desmapeado de la región ya mapeada en memoria. Los campos addr indican la dirección inicial del mapeo y la longitud indica el tamaño en bytes del mapeo que se va a anular. Por lo general, el mapeo y la anulación del mapeo serían para todas las regiones mapeadas. Si tiene que ser diferente, entonces debe encogerse o cortarse en dos partes. Si la dirección no tiene ningún mapeo, esta llamada no tendría ningún efecto y la llamada devuelve 0 (éxito).

Consideremos un ejemplo:

Step 1 - Escriba en el archivo caracteres alfanuméricos como se muestra a continuación -

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- Asigne el contenido del archivo a la memoria usando la llamada al sistema mmap (). Esto devolvería la dirección de inicio después de mapeada en la memoria.

Step 3- Acceda al contenido del archivo usando la notación de matriz (también puede acceder con notación de puntero) ya que no lee la costosa llamada al sistema read (). Al utilizar la asignación de memoria, evite la copia múltiple entre el espacio de usuario, los búferes de espacio del kernel y la memoria caché del búfer.

Step 4 - Repita la lectura del contenido del archivo hasta que el usuario ingrese “-1” (significa fin de acceso).

Step 5 - Realizar actividades de limpieza, es decir, anular el mapeo de la región de memoria asignada (munmap ()), cerrar el archivo y eliminarlo.

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

Salida

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