Memory-mapped file

De Departamento de Informatica
Saltar a: navegación, buscar

Un archivo mapeado a memoria (en inglés Memory-mapped file) es un esquema que permite la asociación o una correlación byte a byte entre un archivo y una porción de la Memoria Virtual, básicamente permiten acceder a archivos de disco a través de punteros de memoria. Esta asociación permite a una aplicación o a algunos procesos ejecutar acciones de lectura y escritura (sobre el archivo) directamente, como si se encontrara en la memoria principal. Para ello, el sistema operativo crea un objeto tipo file-mapping de manera de mantener la asociación, luego se proporcionará una vista para el acceso al archivo de manera que los procesos tengan acceso a este a través de la vista mencionada. Las funciones de "file-mapping" implementadas en la API de Microsoft(por ejemplo) permiten crear objetos "file-mapping" y vistas de acceso para lectura y escritura.[1]

Contenido


Tipos de archivos asignados a memoria

Archivos asignados a memoria persistentes

Cuando hablamos de archivos persistentes nos referimos a los archivos asignados a una memoria los cuales están o se encuentran asociados a un archivo ubicado en un disco(archivo de origen). Cuando se termina de utilizar el archivo mapeado, son guardados sus cambios en el archivo de origen antes mencionado. Se recomienda esta forma cuando se trabajen con archivos de origen muy grandes.

Archivos asignados a memoria no persistentes

Al contrario de los archivos persistentes, estos no estan asociados a un archivo en el disco. Una vez que se terminan de utilizar, se pierden los datos asociados a estos. Este tipo de archivos se utilizan y son ideales para la comunicación entre procesos (IPC), para crear memoria compartida por ejemplo.

Procesos y vistas

Los archivos mapeados a memoria pueden ser compartidos por distintos procesos. Dado un nombre asignado por el proceso que crea el archivo, los demás procesos pueden realizar una asignación a este conociendo el identificador (nombre) asociado previamente.


Vistas superpuestas de un archivo asignado a memoria

Para el trabajo con archivos mapeados a memoria es necesaria la creación de una vista del total o parte del archivo, es decir, los procesos manipularan el fichero a través de las vistas. Es posible la creación de varias vistas de una misma parte del archivo asignado a memoria, creando "memoria simultánea". Para crear vistas simultáneas estas deben ser creadas a partir del mismo archivo mapeado a memoria.
¿Cuando se hace necesaria la creación de varias vistas?, podemos realizar esta acción cuando ciertos archivos pesen o tengan un tamaño mayor que el espacio de memoria lógico disponible para el mapeo de memoria (Por ejemplo: un archivo de 1 GB en un computador de 32-bits).

Para las vistas, existen principalmente dos tipos:

  • Vista de acceso secuencial: Se utiliza para tener acceso secuencial al archivo. Por supuesto, esto es recomendado para archivos que en general son muy pequeños, los archivos no persistentes o las IPC.
  • Vista de acceso aleatorio: Similar a la idea anterior, las vistas de acceso aleatorio se utilizan para tener un acceso aleatorio al archivo. Recomendado para los archivos persistentes.

Finalmente, ¿como se acceden a los archivos mapeados a memoria?, el administrador de memoria del sistema operativo es quien provee el acceso a los archivos mapeados a memoria, entonces el archivo es automáticamente particionado en páginas las cuales son accesadas según la necesidad correspondiente.[2]

¿Cuando se utilizan?

Podemos identificar 3 tipos de tareas para los archivos mapeados en memoria:

Lectura de archivos ejecutables

Mitos como el de que un "ejecutable por ser más grande se demora más en cargar" carecen de sentido cuando hablamos de archivos mapeados en memoria. Para explicar de mejor forma esta afirmación explicaremos lo que en realidad sucede:
Esquema de proyección de un ejecutable sobre la dirección virtual de dos procesos

En primer lugar, se abre el fichero a través del API CreateFile, luego se leen los datos de la cabecera del ejecutable (referencias a funciones, posición en el espacio de memoria, variables estáticas, etc) posteriormente se reserva un espacio de memoria virtual para almacenar el ejecutable, pero se compromete con el mismo espacio físico donde esta el archivo ejecutable, es decir, el "Page Directory" indicará que el rango de páginas reservado cuenta con almacenamiento físico comprometido en el archivo "Programa.exe". Así no se lee todo el ejecutable para volcarlo a memoria, sino que se deja en disco, y cada vez que se lea una nueva instrucción para ejecutarla, se accederá a la página en disco.
Finalmente, por cada instrucción que se ejecute del programa, se tendrá que ir a leer desde la dirección de memoria virtual. Una vez con la instrucción cargada en la memoria física, continuamos con la ejecución.
Como podemos apreciar no se realiza la carga en memoria de la totalidad del ejecutable sino que se realiza a medida que se necesita en la ejecución del programa.

Operaciones de I/O a discos sin buffer de memoria intermedio

Esta característica nos permite editar archivos de disco desde la memoria, es el sistema el que se encarga de llevar lo que leemos y escribimos desde el archivo a memoria y viceversa.

El algoritmo típico para una aplicación que realiza trabajo en el disco es el siguiente:
1.-Comienza la aplicación, se leen los datos del archivo en disco, se procede a volcarlo en estructuras en memoria, como un array de registros, un vector, etc.
2.- Se manipular las estructuras de memoria, según las acciones del usuario.
3.- Luego, al cerrar la aplicación, se graban de nuevo los datos a los archivos en disco.

A pesar de ser una estrategia muy ocupada, posee inconvenientes como:

  • Aumento en el tiempo de lectura/escritura de los archivos a medida que aumenta su tamaño.
  • Inconsistencias a la hora de escribir en un mismo archivo.
  • Se consumen más recursos, ya que necesitamos espacio en RAM, en el archivo de intercambio y en los archivos de datos para almacenar las estructuras donde volcaremos los datos. Sin embargo, con archivos proyectados, no necesitaremos espacio en el archivo de intercambio (porque utilizamos el mismo archivo de datos), y sólo ocuparemos espacio en RAM para aquellas páginas que sean leídas durante la ejecución del programa.
  • Diferencias en rapidez con la "lectura" de archivos de la manera tradicional.

Creación de zonas de memoria compartida

Aunque ya mencionamos ligeramente este tema en la sección "procesos y vistas", se hace necesaria una breve extención al tema.
Una de las mejores utilidades de los archivos mapeados en memoria es la comunicación de datos entre procesos. La plataforma Win32 presenta complicaciones o trabas a la hora de compartir datos entre procesos dado el carácter privativo de su espacio en memoria y del nivel de seguridad que busca un sistema operativo grande como Windows. Para solucionar esto, se ofrecen los siguientes dos métodos utilizando archivos proyectados en memoria:

  • Proyecciones nombradas: Se crea la proyección (vista) de un archivo con un identificador para que luego este pueda ser referenciado desde otro proceso, de manera de generar la "comunicación" entre procesos independientes.
  • Herencia de descriptores: Este método facilita la comunicación entre procesos padre-hijo. Basicamente referencia con un descriptor (handle) a los objetos proyección los cuales pueden heredarse a través de procesos padre e hijo.


Ante la modificación de una archivo, el otro proceso debe enterarse de este evento para que este disponga de los nuevos datos. Además de compartir datos entre procesos, Windows permite desligar estos datos cuando alguno de los procesos hagan modificaciones. Es decir, los datos se comparten inicialmente, y continúan en este estado mientras no hayan sido modificados, cuando alguno de los procesos cambie el contenido de una página, se hará una copia de esta página y las modificaciones se harán sobre la nueva copia. El proceso que modificó el contenido ahora proyectará su memoria sobre la copia de la página, en vez de la página compartida. Este comportamiento se denomina "Copy on Write" o "Copia con escritura" y es uno de los métodos más utilizados en distintos sistemas operativos.[3]

Ejemplos

  • Al igual que para abrir un archivo se crea FILE *file, en el caso de Memory-Mapped comenzamos declarando un int Mapper , para asociar una variable al archivo que vamos a abrir para mapear.
Archivos Memory Mapper
FILE *file; Int Mapper;
file = fopen("filename.txt","w") Mapper = open("Archivo-a-mapear.pdfx", O_RONLY);

Las opciones para abrir el mapeo son: O_RDONLY ( Sólo Lectura ), O_WRONLY ( Sólo Escritura ), O_RDWR ( Lectura y Escritura ).[4]

Luego, para mapear el archivo recién abierto, utilizamos la función mmap():

void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
Tipo y Nombre Parámetro Descripción
void *addr Dirección de memoria del archivo mapeado. Se recomienda dejar que el S.O. determine esto cplocando en el campo la variable (caddr_t)0.
size_t len Tamaño del archivo a mapear. Es recomendado usar getpagesize()
int prot Indica la forma de edición que tendrá este segmento de memoria. Debe ser concordante a la opción que se abrió el archivo a mapear, como se indica anteriormente y las opciones se pueden encontrar en las referencias. [5]
int flags Los flags sirven para diversos mótivos, como compartir o no el contenido de la memoria con otros procesos. ( MAP_SHARED o MAP_PRIVATE ). [6]
int fildes Nombre del archivo abierto para mapear, en este caso Mapper
off_t off Es el offset del archivo a mapear. Debe ser múltiplo del tamaño de página de la memoria virtual. Se recomienda usar getpagesize()

Ejemplo:

int pagesize = getpagesize();
mapeo = mmap( (caddr_t)0 , pagesize, PROT_READ , MAP_PRIVATE , Mapper , pagesize );

A pesar de ser una función void, mmap devuelve la dirección de memoria asociada al archivo mapeado. Luego, con lo anterior, ya se tiene mapeado el "Archivo-a-mapear.pdfx".

Finalmente, para terminar el mapeo de memoria se usa:

int munmap(caddr_t addr, size_t len);

La que devuelve -1 en caso de problemas y 0 en caso de exito.

Ejemplo:

munmap( mapeo, pagesize );

Todo lo anterior se encuentra en el lenguaje C, en la librería sys/mman.h .[7] [8]

Referencias

Herramientas personales
Espacios de nombres
Variantes
Acciones
Navegación
Herramientas