example - ¿Cuándo debo usar mmap para acceder a archivos?
mmap c example (5)
Los entornos POSIX proporcionan al menos dos formas de acceder a los archivos. Existe el sistema estándar de llamadas open()
, read()
, write()
y friends, 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?
Además de otras respuestas agradables, una cita de la programación del sistema Linux escrita por el experto Robert Love de Google:
Ventajas de
mmap( )
Manipular archivos a través de
mmap( )
tiene algunas ventajas sobre las llamadas estándar de sistema deread( )
ywrite( )
. Entre ellos están:
La lectura y escritura en un archivo asignado en memoria evita la copia extraña que se produce al usar las llamadas del sistema
read( )
owrite( )
, donde los datos se deben copiar hacia y desde un búfer de espacio de usuario.Aparte de los posibles fallos de la página, la lectura y la escritura en un archivo asignado en memoria no suponen una sobrecarga de llamadas al sistema o de cambio de contexto. Es tan sencillo como acceder a la memoria.
Cuando varios procesos asignan el mismo objeto en la memoria, los datos se comparten entre todos los procesos. Las asignaciones de escritura compartidas y de solo lectura se comparten en su totalidad; Las asignaciones de escritura privada tienen sus páginas aún no COW (copia en escritura) compartidas.
La búsqueda en torno al mapeo implica manipulaciones de punteros triviales. No hay necesidad de la llamada al sistema
lseek( )
.Por estas razones,
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 son siempre un número entero de páginas en 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 vacío. Para archivos pequeños, se puede desperdiciar un porcentaje significativo de la asignación. Por ejemplo, con páginas de 4 KB, un mapeo de 7 bytes desperdicia 4.089 bytes.
Las asignaciones de memoria deben encajar 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 resultar en 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.
Existe 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 mediante la eliminación de la copia doble mencionada en la sección anterior, particularmente para archivos más grandes y de acceso frecuente.
Por estas razones, los beneficios de
mmap( )
semmap( )
mejor 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 de manera uniforme por la Tamaño de página (y por lo tanto no hay espacio desperdiciado).
El mapeo de memoria tiene el potencial de una gran ventaja de velocidad en comparación con la IO tradicional. Permite que el sistema operativo lea los datos del archivo de origen cuando se tocan las páginas del archivo asignado en la memoria. Esto funciona al crear páginas con fallas, que el sistema operativo detecta y luego el sistema carga automáticamente los datos correspondientes del archivo.
Esto funciona de la misma manera que el mecanismo de paginación y generalmente se optimiza para E / S de alta velocidad al leer los datos sobre los límites y tamaños de la página del sistema (generalmente 4K), un tamaño para el cual la mayoría de las cachés del sistema de archivos están optimizadas.
Un área donde encontré que mmap () no era una ventaja era cuando leía archivos pequeños (bajo 16K). La sobrecarga de la página que fallaba para leer el archivo completo era muy alta en comparación con solo hacer una sola llamada al sistema read (). Esto se debe a que el núcleo a veces puede satisfacer una lectura completamente en su porción de tiempo, lo que significa que su código no se aleja. Con un error de página, parecía más probable que se programara otro programa, lo que hace que la operación del archivo tenga una mayor latencia.
mmap es excelente si tiene múltiples procesos que acceden a los datos de una sola lectura desde el mismo archivo, lo cual 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 una gran cantidad de memoria.
mmap también permite que el sistema operativo optimice las operaciones de paginación. Por ejemplo, considere dos programas; el programa A, que lee en un archivo de 1 MB en un búfer que crea con malloc, y el programa B, que coloca el archivo de 1 MB en la 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 poder reutilizar la memoria. En el caso de B, cualquier página mmap''d no modificada puede reutilizarse de inmediato porque el sistema operativo sabe cómo restaurarlas desde el archivo existente desde el que fueron mmap''d. (El sistema operativo puede detectar qué páginas no se modifican marcando inicialmente las páginas de mmaptables que se pueden escribir como de solo lectura y detectando fallas seg, similares a la estrategia Copiar en escritura).
mmap también es útil para la comunicación entre procesos. Puede hacer un mmap de un archivo como lectura / escritura en los procesos que necesitan comunicarse y luego usar primitivas de sincronización en la región mmap''d (para eso es el indicador MAP_HASSEMAPHORE).
Un lugar donde mmap puede ser incómodo es si necesita trabajar con archivos muy grandes en una máquina de 32 bits. Esto se debe a que mmap tiene que encontrar un bloque de direcciones contiguo en el espacio de direcciones de su proceso que sea lo suficientemente grande como para adaptarse a todo el rango del archivo que se está asignando. Esto puede convertirse en un problema si su espacio de direcciones se fragmenta, donde puede tener 2 GB de espacio de direcciones libres, pero ningún rango individual puede ajustarse a una asignación de archivos de 1 GB. En este caso, es posible que tenga que asignar el archivo en partes más pequeñas de las que le gustaría que encajen.
Otra incomodidad potencial con mmap como reemplazo de la lectura / escritura es que tiene que comenzar su mapeo en las compensaciones del tamaño de la página. Si solo desea obtener algunos datos en la compensación X, deberá corregir esa compensación para que sea compatible con mmap.
Y, por último, leer / escribir es la única forma en que puede trabajar con algunos tipos de archivos. mmap no se puede utilizar en cosas como tuberías y ttys.
mmap
tiene la ventaja de tener acceso aleatorio en archivos grandes. Otra ventaja es que accede a él con operaciones de memoria (memcpy, puntero aritmético), sin molestarse con el búfer. La E / S normal a veces puede ser bastante difícil cuando se usan buffers cuando tienes estructuras más grandes que tu buffer. El código a manejar que a menudo es difícil de entender, el mmap es generalmente más fácil. Dicho esto, hay ciertas trampas cuando se trabaja con mmap
. Como la gente ya ha mencionado, el mmap
es bastante costoso de configurar, por lo que vale la pena usarlo solo para un tamaño determinado (que varía de una máquina a otra).
Para los accesos puramente secuenciales al archivo, tampoco es siempre la mejor solución, aunque una llamada apropiada a madvise
puede mitigar el problema.
Debe tener cuidado con las restricciones de alineación de su arquitectura (SPARC, itanium); con IO de lectura / escritura, los búferes a menudo están alineados correctamente y no quedan atrapados al eliminar la referencia de un puntero fundido.
También debe tener cuidado de no acceder fuera del mapa. Puede suceder fácilmente si usa funciones de cadena en su mapa y su archivo no contiene un / 0 al final. Funcionará la mayor parte del tiempo cuando su tamaño de archivo no sea un múltiplo del tamaño de la página, ya que la última página se rellena con 0 (el área asignada siempre tiene el tamaño de un múltiplo de su tamaño de página).