Necesito la forma más rápida de sincronizar periódicamente el archivo con la memoria.
Lo que creo que me gustaría es tener un archivo mmap’d, que solo se sincroniza con el disco manualmente. No estoy seguro de cómo evitar que ocurra una sincronización automática.
El archivo no se puede modificar excepto en los tiempos que especifique manualmente. El punto es tener un archivo de punto de control que mantenga una instantánea del estado en la memoria. Me gustaría evitar copiar tanto como sea posible, ya que será necesario llamar con bastante frecuencia y la velocidad es importante.
Cualquier cosa que escribas en la memoria dentro de un MAP_SHARED
la asignación de un archivo se considera escrita en el archivo en ese momento, como si hubiera utilizado write()
. msync()
en este sentido es completamente análoga a fsync()
– simplemente asegura que los cambios que ha realizado ya está hecho al archivo se envían al almacenamiento permanente. No puedes cambiar esto, es cómo mmap()
está definido para trabajar.
En general, la forma segura de hacer esto es escribir una copia coherente completa de los datos en un archivo temporal, sincronizar el archivo temporal y luego renombrarlo atómicamente sobre el archivo de punto de control anterior. Esta es la única forma de asegurarse de que un bloqueo entre los puntos de control no lo deje con un archivo inconsistente. Cualquier solución que haga menos copias requerirá un formato de archivo de estilo de registro de transacciones más complicado y será más intrusivo para el resto de su aplicación (requiriendo que se invoquen ganchos específicos en cada lugar donde se cambia el estado en memoria) .
mmap
no se puede utilizar para este propósito. No hay forma de evitar que los datos se escriban en el disco. En la práctica, usando mlock()
para hacer que la memoria no se pueda intercambiar puede que tiene el efecto secundario de evitar que se escriba en el disco, excepto cuando solicite que se escriba, pero no hay garantía. Ciertamente, si otro proceso abre el archivo, verá la copia en caché en la memoria (con sus últimos cambios), no la copia en el disco físico. En muchos sentidos, lo que debe hacer depende de si está tratando de sincronizar con otros procesos o simplemente por seguridad en caso de falla o corte de energía.
Si el tamaño de sus datos es pequeño, puede probar otros métodos para la sincronización atómica con el disco. Una forma es almacenar todo el conjunto de datos en un nombre de archivo y crear un archivo vacío con ese nombre, luego eliminar el archivo anterior. Si existen 2 archivos al inicio (debido a un tiempo de bloqueo extremadamente improbable), elimine el anterior y reanude desde el más nuevo. write()
mayo también sea atómico si el tamaño de sus datos es más pequeño que un bloque de sistema de archivos, tamaño de página o bloque de disco, pero no conozco ninguna garantía en ese sentido. Tendrías que investigar un poco.
Otro enfoque muy estándar que funciona siempre que sus datos no sean tan grandes que 2 copias no quepan en el disco: simplemente cree una segunda copia con un nombre temporal, luego rename()
encima de la anterior. rename()
siempre es atómico. Este es probablemente el mejor enfoque a menos que tenga una razón para no hacerlo de esa manera.

James Caccese
Tú podrías mmap()
el archivo como copia en escritura para que cualquier actualización que haga en la memoria no se vuelva a escribir en el archivo, luego, cuando desee sincronizar, podría:
A) Cree una nueva asignación de memoria que no se copie al escribir y copie solo las páginas que modificó en ella.
O
B) Abra el archivo (archivo normal abierto) con E/S directa (lectura y escritura de tamaño de bloque alineado) y escriba solo las páginas que modificó. La E/S directa sería agradable y rápida porque está escribiendo páginas completas (el tamaño de la página de memoria es un múltiplo del tamaño del bloque del disco) y no hay almacenamiento en búfer. Este método tiene la ventaja de no utilizar el espacio de direcciones en caso de que su mmap()
es grande y no hay espacio para mmap()
otro archivo enorme.
Después de la sincronización, su copia en escritura mmap()
es el mismo que su archivo de disco, pero el núcleo aún tiene las páginas que necesitaba sincronizar marcadas como no compartidas (con el disco). Entonces puede cerrar y volver a crear el mmap()
(todavía copiar en escritura) de esa manera, el kernel puede descartar sus páginas si es necesario (en lugar de paginarlas para intercambiar espacio) si hay presión de memoria.
Por supuesto, tendría que hacer un seguimiento de las páginas que usted mismo modificó porque no puedo pensar en cómo obtendría acceso a dónde el sistema operativo guarda esa información. (¿No sería útil syscall()
?)
— editar —
En realidad, consulte ¿Se puede encontrar la suciedad de las páginas de un mmap desde el espacio de usuario? para obtener ideas sobre cómo ver qué páginas están sucias.

llasram
Como han sugerido los otros encuestados, no creo que haya una forma portátil de hacer lo que quieras sin copiar. Si está buscando hacer esto en un entorno de propósito especial donde puede controlar el sistema operativo, etc., puede hacerlo en Linux con el sistema de archivos btrfs.
btrfs admite un nuevo reflink()
operación que es esencialmente una copia del sistema de archivos de copia en escritura. Tú podrías reflink()
su archivo a un temporal en el inicio, mmap()
lo temporal, entonces msync()
y reflink()
el temporal de vuelta al original al punto de control.

ganso nate
Sospecho que ningún sistema operativo puede aprovechar eso, pero sería posible que un sistema operativo notara optimizaciones para:
int fd = open("file", O_RDWR | O_SYNC | O_DIRECT);
size_t length = get_lenght(fd);
uint8_t * map_addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
...
// This represents all of the changes that could possibly happen before you
// want to update the on disk file.
change_various_data(map_addr);
if (is_time_to_update()) {
write(fd, map_addr, length);
lseek(fd, 0, SEEK_SET);
// you could have just used pwrite here and not seeked
}
Las razones por las que un sistema operativo pudo posiblemente aproveche esto es que hasta que escriba en una página en particular (y nadie más lo hizo), el sistema operativo probablemente solo use la página del archivo real en esa ubicación como intercambio para esa página.
Luego, cuando escribiste en algún conjunto de esas páginas, el sistema operativo Dupdo En Escribe esas páginas para su proceso, pero mantenga las páginas no escritas respaldadas por el archivo original.
Entonces, al llamar write
el sistema operativo pudo notar que la escritura estaba alineada en bloques tanto en la memoria como en el disco, y luego pudo notar que algunas de las páginas de la memoria de origen ya estaban sincronizadas con las páginas exactas del sistema de archivos en las que se estaban escribiendo y solo escribieron el páginas que habían cambiado.
Habiendo dicho todo eso, no me sorprendería si ningún sistema operativo realiza esta optimización, y este tipo de código termina siendo muy lento y causa mucha escritura en el disco cuando llama ‘escribir’. Sería genial si se aprovechara.