¿Cuándo debería usar mmap para acceder a archivos?

Los entornos POSIX proporcionan al menos dos formas de acceder a los archivos. Están las llamadas estándar del sistema open() , read() , write() y amigos, pero también existe la opción de usar mmap() para asignar el archivo a la memoria virtual.

¿Cuándo es preferible usar uno sobre el otro? ¿Cuáles son sus ventajas individuales que merecen incluir dos interfaces?

mmap es excelente si tiene múltiples procesos accediendo a los datos de manera solo lectura desde el mismo archivo, que es común en el tipo de sistemas de servidor que escribo. mmap permite que todos esos procesos compartan las mismas páginas de memoria física, ahorrando mucha memoria.

mmap también permite que el sistema operativo optimice las operaciones de búsqueda. Por ejemplo, considere dos progtwigs; progtwig A que lee en un archivo de 1MB en una creación de búfer con malloc, y progtwig B que mmaps el archivo de 1MB en memoria. Si el sistema operativo tiene que intercambiar parte de la memoria de A, debe escribir el contenido del búfer para intercambiar antes de que pueda reutilizar la memoria. En el caso de B, cualquier página mmap sin modificar se puede reutilizar inmediatamente porque el sistema operativo sabe cómo restaurarla desde el archivo existente desde el que se mapeó. (El sistema operativo puede detectar qué páginas no se han modificado marcando inicialmente las páginas mmap’d grabables como de solo lectura y capturando las fallas seg, similar a la estrategia Copiar al escribir).

mmap también es útil para la comunicación entre procesos. Puede mapear un archivo como de lectura / escritura en los procesos que necesitan comunicarse y luego usar primitivas de sincronización en la región mmap’d (para esto es el indicador MAP_HASSEMAPHORE).

Un lugar mmap puede ser incómodo si necesita trabajar con archivos muy grandes en una máquina de 32 bits. Esto se debe a que mmap debe encontrar un bloque contiguo de direcciones en el espacio de direcciones de su proceso que sea lo suficientemente grande como para ajustarse a todo el rango del archivo que se está mapeando. Esto puede convertirse en un problema si el espacio de direcciones se fragmenta, donde puede tener 2 GB de espacio libre de direcciones, pero ningún rango individual puede ajustarse a un mapeo de archivos de 1 GB. En este caso, es posible que deba mapear el archivo en trozos más pequeños de lo que le gustaría que encajara.

Otra posible incomodidad con mmap como reemplazo de lectura / escritura es que debe comenzar su asignación en las compensaciones del tamaño de la página. Si solo quieres obtener algunos datos en offset X, necesitarás corregir ese offset para que sea compatible con mmap.

Y finalmente, leer / escribir es la única forma en que puede trabajar con algunos tipos de archivos. mmap no se puede usar en cosas como pipes y ttys.

Un área donde encontré que mmap () no era una ventaja era cuando se leían archivos pequeños (menos de 16K). La sobrecarga de la página al leer todo el archivo fue muy alta en comparación con solo hacer una sola llamada al sistema de lectura (). Esto se debe a que el kernel a veces puede satisfacer una lectura completa en su porción de tiempo, lo que significa que su código no cambia. Con un error de página, parecía más probable que se progtwigra otro progtwig, lo que hacía que la operación de archivo tuviera una latencia más alta.

mmap tiene la ventaja cuando tienes acceso aleatorio en archivos grandes. Otra ventaja es que accede a ella con operaciones de memoria (memcpy, aritmética del puntero), sin molestarse con el almacenamiento en búfer. La E / S normal a veces puede ser bastante difícil cuando se utilizan almacenamientos intermedios cuando se tienen estructuras más grandes que el almacenamiento intermedio. El código que se maneja a menudo es difícil de corregir, mmap es generalmente más fácil. Dicho esto, hay ciertas trampas cuando se trabaja con mmap . Como ya se mencionó, mmap es bastante costoso de configurar, por lo que vale la pena usarlo solo para un tamaño dado (que varía de una máquina a otra).

Para accesos secuenciales puros al archivo, tampoco siempre es la mejor solución, aunque una llamada apropiada a la madvise puede mitigar el problema.

Debe tener cuidado con las restricciones de alineación de su architecture (SPARC, itanium), con IO de lectura / escritura, los búferes a menudo están alineados correctamente y no se atrapan al desreferenciar un puntero emitido.

También debe tener cuidado de no acceder fuera del mapa. Puede suceder fácilmente si utiliza funciones de cadena en su mapa, y su archivo no contiene un \ 0 al final. Funciona la mayor parte del tiempo cuando el tamaño de su archivo no es un múltiplo del tamaño de la página, ya que la última página está llena de 0 (el área asignada siempre tiene el tamaño de un múltiplo del tamaño de su página).

Además de otras buenas respuestas, una cita de la progtwigción del sistema Linux escrita por el experto de Robert Love:

Ventajas de mmap( )

La manipulación de archivos a través de mmap( ) tiene un puñado de ventajas sobre las llamadas estándar al sistema de read( ) y write( ) . Entre ellos están:

  • Leer y escribir en un archivo mapeado en memoria evita la copia extraña que se produce cuando se utilizan las llamadas al sistema read( ) o write( ) , donde los datos deben copiarse desde y hacia un buffer de espacio de usuario.

  • Además de cualquier posible error de página, leer y escribir en un archivo mapeado en memoria no implica ninguna llamada al sistema o sobrecarga del cambio de contexto. Es tan simple como acceder a la memoria.

  • Cuando múltiples procesos mapean el mismo objeto en la memoria, los datos se comparten entre todos los procesos. Las asignaciones de escritura compartida y de solo lectura se comparten en su totalidad; las asignaciones privadas de escritura tienen compartidas sus páginas aún no publicadas (copy-on-write).

  • Buscar alrededor del mapeo implica manipulaciones triviales del puntero. No hay necesidad de la llamada al sistema lseek( ) .

Por estos motivos, mmap( ) es una opción inteligente para muchas aplicaciones.

Desventajas de mmap( )

Hay algunos puntos a tener en cuenta al usar mmap( ) :

  • Las asignaciones de memoria siempre son un número entero de páginas de tamaño. Por lo tanto, la diferencia entre el tamaño del archivo de respaldo y un número entero de páginas se “desperdicia” como espacio suelto. Para archivos pequeños, un porcentaje significativo de la asignación puede desperdiciarse. Por ejemplo, con páginas de 4 KB, una asignación de 7 bytes desperdicia 4.089 bytes.

  • Las asignaciones de memoria deben caber en el espacio de direcciones del proceso. Con un espacio de direcciones de 32 bits, un gran número de asignaciones de varios tamaños puede provocar la fragmentación del espacio de direcciones, lo que dificulta la búsqueda de grandes regiones contiguas libres. Este problema, por supuesto, es mucho menos evidente con un espacio de direcciones de 64 bits.

  • Hay una sobrecarga en la creación y el mantenimiento de las asignaciones de memoria y las estructuras de datos asociadas dentro del kernel. Esta sobrecarga generalmente se evita al eliminar la copia doble mencionada en la sección anterior, particularmente para archivos más grandes y accedidos frecuentemente.

Por estas razones, los beneficios de mmap( ) se realizan en gran medida cuando el archivo mapeado es grande (y por lo tanto, cualquier espacio desperdiciado es un pequeño porcentaje del mapeo total), o cuando el tamaño total del archivo mapeado es divisible uniformemente por el tamaño de página (y por lo tanto no hay espacio desperdiciado).

El mapeo de memoria tiene un potencial para una gran ventaja de velocidad en comparación con IO tradicional. Permite que el sistema operativo lea los datos del archivo fuente a medida que se tocan las páginas del archivo asignado a la memoria. Esto funciona creando páginas con fallas, que el SO detecta y luego el sistema operativo carga los datos correspondientes del archivo automáticamente.

Funciona de la misma manera que el mecanismo de búsqueda y generalmente está optimizado para E / S de alta velocidad leyendo datos en los límites y tamaños de la página del sistema (generalmente 4K), un tamaño para el cual la mayoría de los cachés del sistema de archivos están optimizados.