2012-01-02 14 views
7

Necesito un tamaño de copia de un archivo mmap muy grande al tiempo que permite el acceso simultáneo a los hilos del lector.Cambio de tamaño rápido de un archivo mmap

La manera simple es usar dos asignaciones MAP_SHARED (crecer el archivo, luego crear una segunda asignación que incluya la región crecida) en el mismo proceso sobre el mismo archivo y luego desasignar el mapeo antiguo una vez que todos los lectores puedan acceder estan terminados. Sin embargo, tengo curiosidad por saber si el esquema a continuación podría funcionar, y si es así, ¿hay alguna ventaja?

  1. mmap un archivo con MAP_PRIVATE
  2. do acceso de sólo lectura a esta memoria en múltiples hilos
  3. o bien adquirir un mutex para el archivo, escribir en la memoria (se supone esto se hace de una manera que la los lectores, que pueden estar leyendo esa memoria, no se equivocan)
  4. o adquieren el mutex, pero aumentan el tamaño del archivo y usan mremap para moverlo a una nueva dirección (cambiar el tamaño de la asignación sin copiar o archivo innecesario IO.)

La parte loca aparece en (4). Si mueve la memoria, las direcciones antiguas se vuelven inválidas y los lectores que todavía la están leyendo pueden tener una infracción de acceso repentinamente. ¿Qué pasa si modificamos los lectores para atrapar esta violación de acceso y luego reiniciamos la operación (es decir, no volver a leer la dirección incorrecta, volver a calcular la dirección dada la compensación y la nueva dirección base de mremap). Sí, sé que eso es malo pero, en mi opinión, los lectores solo pueden leer con éxito los datos en la dirección anterior o pueden fallar con una infracción de acceso y volver a intentarlo. Si se tiene suficiente cuidado, debe estar seguro. Como el redimensionamiento no ocurriría a menudo, los lectores eventualmente tendrían éxito y no se quedarían atrapados en un ciclo de reintento.

Puede producirse un problema si se vuelve a utilizar ese antiguo espacio de direcciones mientras el lector todavía tiene un puntero al mismo. Entonces no habrá violación de acceso, pero los datos serán incorrectos y el programa ingresará a la tierra unicornio y llena de dulces de comportamiento indefinido (donde generalmente no hay unicornios ni caramelos).

Pero si controlas las asignaciones completamente y podrías asegúrese de que las asignaciones que suceden durante este período no vuelvan a usar ese espacio de direcciones anterior, entonces esto no debería ser un problema y el comportamiento no debería estar indefinido.

¿Estoy en lo cierto? Podría esto funcionar? ¿Hay alguna ventaja en esto sobre el uso de dos asignaciones MAP_SHARED?

+0

Puede usar un bloqueo de lectura/escritura y proteger la reasignación bajo el bloqueo de escritura, ¿no? – fge

+0

Supongo que el cuello de botella es el disco. ¿Estás seguro de que vale la pena el problema? copiar archivos enormes del disco siempre lleva tiempo, porque los discos son dispositivos mecánicos lentos. –

+0

fge, sí, pero el bloqueo de los hilos de lectura está fuera de cuestión en este caso – Eloff

Respuesta

4

Me resulta difícil imaginar un caso en el que no se conoce el límite superior de cuán grande puede ser el archivo. Suponiendo que sea cierto, puede "reservar" el espacio de direcciones para el tamaño máximo del archivo proporcionando ese tamaño cuando el archivo se asigna por primera vez con mmap(). Por supuesto, cualquier acceso más allá del tamaño real del archivo provocará una violación de acceso, pero así es como desea que funcione de todos modos: podría argumentar que reservar el espacio de dirección adicional garantiza la violación de acceso en lugar de dejar ese rango de direcciones abierto para ser utilizado por otras llamadas a cosas como mmap() o malloc().

De todos modos, el punto es que con mi solución, nunca mueve el rango de direcciones, solo cambia su tamaño y ahora su bloqueo está alrededor de la estructura de datos que proporciona el tamaño válido actual para cada hilo.

Mi solución no funciona si tienes tantos archivos que la asignación máxima para cada archivo te deja sin espacio de direcciones, pero esta es la edad del espacio de direcciones de 64 bits, así que con suerte tu tamaño máximo de mapeo no es problema.

(Solo para asegurarme de que no estaba olvidando algo estúpido, escribí un pequeño programa para convencerme a mí mismo que crear una asignación más grande que el tamaño de archivo da una infracción de acceso cuando intentas acceder más allá del tamaño del archivo, y luego funciona bien una vez que ftruncate() el archivo a ser más grande, todos con la misma dirección devuelta desde la primera llamada de mmap().

+0

Esta fue mi primera idea, pero lamentablemente el espacio de direcciones está limitado a 8TB en los sistemas operativos modernos, lo que significa que si hay una gran cantidad de archivos mapeados de memoria reservando todo el espacio posible máximo, puede quedarse sin espacio de direcciones. Se podría usar mmap con MAP_NORESERVE para reservar la mayor cantidad de espacio de direcciones posible al inicio del programa, y ​​luego asignar desde allí con MAP_FIXED, distribuyéndolo de manera equitativa al principio y luego tomar de los mapas que lo usan menos al agotarse. Luego use la estrategia anterior o memcpy estándar si un mmap se queda sin espacio y no hay espacio adyacente reservado. – Eloff

+0

de dónde obtienes tu figura de 8TB? – camelccc

+2

Linux puede administrar 64TB de memoria phsyiscal dentro de un espacio de direcciones de 128TB. No estoy seguro si tiene muchos archivos que son más grandes que 128TB (incluso el volumen RAID completo en mi red compartida es de solo 24TB, pero su millaje puede variar). Usted no es Google, ¿verdad? – Damon

Cuestiones relacionadas