fflush, fsync y sync vs capas de memoria

10 minutos de lectura

Avatar de usuario de Antonino
antonino

Sé que ya hay preguntas similares y les di un vistazo, pero no pude encontrar una respuesta unívoca explícita a mi pregunta. Estaba investigando en línea sobre estas funciones y su relación con las capas de memoria. En particular encontré este hermoso artículo eso me dio una buena idea sobre las capas de memoria

capas de memoria

Parece que fflush() mueve los datos de la aplicación al búfer del sistema de archivos del kernel y está bien, todos parecen estar de acuerdo en este punto. Lo único que me dejó desconcertado fue que en el mismo artículo asumieron un caché de reescritura diciendo que con fsync() “los datos se guardan en la capa de almacenamiento estable” y luego agregaron que “el almacenamiento puede almacenar los datos en un caché de reescritura, por lo que fsync() todavía se requiere para los archivos abiertos con O_DIRECT para guardar los datos en un almacenamiento estable”

Leyendo aquí y allí parece que la verdad es que fsync() y sync() deje que los datos ingresen al dispositivo de almacenamiento, pero si este tiene capas de almacenamiento en caché, simplemente se mueve aquí, no de inmediato al almacenamiento permanente y los datos pueden incluso perderse si hay una falla de energía. A menos que tengamos un sistema de archivos con barreras habilitadas y luego “sync()/fsync() y algunas otras operaciones harán que se envíen al dispositivo los comandos correspondientes CACHE FLUSH (ATA) o SYNCHRONIZE CACHE (SCSI)” [from your website answer]

Preguntas:

  1. si los datos que se actualizarán ya están en los búferes del kernel y mi dispositivo tiene una capa de caché volátil en modo de reescritura, ¿es cierto, como dice el artículo, que operaciones como fsync() [and sync() I suppose] sincronizar datos con la capa de memoria estable omitiendo la volátil? Creo que esto es lo que sucede con un caché de escritura simultánea, no con uno de escritura diferida. Por lo que leí, entendí que con un caché de reescritura en fsync() puede simplemente enviar datos al dispositivo que los colocará en el caché volátil y solo ingresarán a la memoria permanente después de

  2. yo lei eso fsync() funciona con un descriptor de archivo y luego con un solo archivo mientras sync() provoca una implementación total de los búferes, por lo que se aplica a todos los datos que se actualizarán. y de esto página también eso fsync() espera el final de la escritura en el disco mientras sync() no espera el final de la escritura real en el disco. ¿Hay otras diferencias relacionadas con las transferencias de datos de memoria entre los dos?

Gracias a los que intentarán ayudar

avatar de usuario de gstukelj
gstukelj

1. Como concluyó correctamente de su investigación fflush sincroniza el espacio de usuario almacenado en búfer datos a nivel de kernel caché (ya que está trabajando con FILE objetos que residen a nivel de usuario y son invisibles para el núcleo), mientras que fsync o sync (trabajando directamente con descriptores de archivo) sincronizar los datos almacenados en caché del kernel con el dispositivo. Sin embargo, este último viene sin garantía de que los datos se hayan escrito realmente en el dispositivo de almacenamiento, ya que estos generalmente también vienen con sus propios cachés. Esperaría lo mismo para msync llamado con MS_SYNC bandera también.

En relación con esto, encuentro la distinción entre sincronizado y sincrónico Operaciones muy útiles a la hora de hablar del tema. Así es cómo Roberto amor lo dice sucintamente:

Una operación de escritura síncrona no regresa hasta que los datos escritos estén, al menos, almacenados en el caché del búfer del núcleo. […] Una operación sincronizada es más restrictiva y más segura que una operación meramente síncrona. Una operación de escritura sincronizada vacía los datos en el disco, lo que garantiza que los datos en el disco estén siempre sincronizados con respecto a los búfer del núcleo correspondientes.

Con eso en mente puedes llamar open con O_SYNC bandera (junto con alguna otra bandera que abre el archivo con un permiso de escritura) para hacer cumplir las operaciones de escritura sincronizadas. Nuevamente, como asumiste correctamente, esto funcionará solo con WRITE THROUGH política de almacenamiento en caché de disco, que en realidad equivale a deshabilitar almacenamiento en caché de disco.

Puede leer esta respuesta sobre cómo deshabilitar el almacenamiento en caché del disco en Linux. Asegúrese de comprobar también este sitio web que también cubre los dispositivos basados ​​en SCSI además de los basados ​​en ATA (para leer sobre los diferentes tipos de discos, consulte este página sobre Microsoft SQL Server 2005última actualización: 19 de abril de 2018).

Hablando de eso, es muy informativo leer acerca de cómo se trata el tema en maquinas windows:

Para abrir un archivo para E/S sin búfer, llame a la función CreateFile con los indicadores FILE_FLAG_NO_BUFFERING y FILE_FLAG_WRITE_THROUGH. Esto evita que el contenido del archivo se almacene en caché y vacía los metadatos en el disco con cada escritura. Para obtener más información, consulte CreateFile.

Aparentemente, así es como Microsoft SQL Server 2005 La familia garantiza la integridad de los datos:

Todas las versiones de SQL Server abren los archivos de registro y datos mediante la función CreateFile de Win32. El miembro dwFlagsAndAttributes incluye la opción FILE_FLAG_WRITE_THROUGH cuando lo abre SQL Server. […]
Esta opción le indica al sistema que escriba a través de cualquier caché intermedia y vaya directamente al disco. El sistema aún puede almacenar en caché las operaciones de escritura, pero no puede vaciarlas de forma perezosa.

Estoy diciendo que esto es informativo en particular debido a esto publicación de blog de 2012 mostrando que algunos discos SATA ignorar el FILE_FLAG_WRITE_THROUGH! No sé cuál es el estado actual de las cosas, pero parece que para garantizar que la escritura en un disco esté realmente sincronizada, debe:

  1. Deshabilite el almacenamiento en caché del disco usando los controladores de su dispositivo.
  2. Asegúrese de que el dispositivo específico que está utilizando admita la política de escritura simultánea/sin almacenamiento en caché.

Sin embargo, si está buscando una garantía de integridad de los datos, puede comprar un disco con su propia fuente de alimentación basada en batería que va más allá de los condensadores (que generalmente solo es suficiente para completar los procesos de escritura en curso). Como se pone en la conclusión en el artículo del blog mencionado anteriormente:

En pocas palabras, use discos de clase empresarial para sus datos y archivos de registro de transacciones. […] En realidad, la situación no es tan dramática como parece. Muchos controladores RAID tienen memoria caché respaldada por batería y no es necesario cumplir con el requisito de escritura simultánea.

2. Para responder (parcialmente) a la segunda pregunta, esto es de las páginas man SYNC(2):

De acuerdo con la especificación estándar (p. ej., POSIX.1-2001), sync() programa las escrituras, pero puede regresar antes de que se realice la escritura real. Sin embargo, desde la versión 1.3.20, Linux realmente espera. (Esto aún no garantiza la integridad de los datos: los discos modernos tienen grandes cachés).

Esto implicaría que fsync y sync funcionan de manera diferente, sin embargo, tenga en cuenta que ambos están implementados en unistd.h lo que sugiere cierta coherencia entre ellos. Sin embargo, yo seguiría Roberto amor quien no recomienda usar sync syscall al escribir su propio código.

El único uso real de sync() está en la implementación de la utilidad de sincronización. Las aplicaciones deben usar fsync() y fdatasync() para enviar al disco los datos de solo los descriptores de archivo requeridos. Tenga en cuenta que sync() puede tardar varios minutos o más en completarse en un sistema ocupado.

“No tengo ninguna solución, pero ciertamente admiro el problema”.

Por lo que leo de tus buenas referencias, es que no hay un estándar. El estándar termina en algún lugar del núcleo. El núcleo controla el controlador del dispositivo y el controlador del dispositivo (posiblemente proporcionado por el fabricante del disco) controla el disco a través de una API (el dispositivo tiene una pequeña computadora a bordo). Es posible que el fabricante haya agregado condensadores/baterías con la potencia suficiente para vaciar los búferes de su dispositivo en caso de un corte de energía, o puede que no lo haya hecho. El dispositivo puede proporcionar una función de sincronización, pero no se sabe si esto realmente sincroniza (vacía) los búferes del dispositivo (depende del dispositivo). Entonces, a menos que seleccione e instale un dispositivo de acuerdo con sus especificaciones (y verifique esas especificaciones), nunca estará seguro.

Este es un problema justo. Incluso después de manejar las condiciones de error, no está seguro de la presencia de datos en su almacenamiento.

¡La página de manual de fsync explica este problema claramente! 🙂 Para aplicaciones que requieren garantías más estrictas sobre la integridad de sus datos, Mac OS X proporciona el fcntl F_FULLFSYNC. El fcntl F_FULLFSYNC le pide a la unidad que vacíe todos los datos almacenados en el búfer al almacenamiento permanente.

Las aplicaciones, como las bases de datos, que requieren un orden estricto de escrituras deben usar F_FULLFSYNC para garantizar que sus datos se escriban en el orden esperado. Consulte fcntl(2) para obtener más detalles.

Sí, fflush() garantiza que los datos dejen el espacio de la memoria del proceso, pero pueden estar en páginas sucias de RAM en espera de una reescritura. Esta es una prueba contra el aborto de la aplicación, pero no contra el bloqueo del sistema o la falla de energía. ¡Incluso si la energía está respaldada, el sistema podría fallar debido a alguna vulnerabilidad de software! Como se mencionó en otras respuestas/comentarios, hacer que los datos de las páginas sucias se escriban en el disco magnéticamente o lo que sea que haga SSD, no atascados en un búfer volátil en el controlador de disco o la unidad, es una combinación de las llamadas correctas o las opciones abiertas y los controladores correctos y dispositivos! Las llamadas le brindan más control sobre los gastos generales, escribiendo más de forma masiva al final de una transacción.

RDBMS, por ejemplo, debe preocuparse no solo por la base de datos que contiene los archivos, sino aún más por los archivos de registro que permiten la recuperación, tanto después de la pérdida del disco como en cualquier reinicio de RDBMS después de un bloqueo. De hecho, algunos pueden estar más sincronizados en el registro que en la base de datos, para preservar la velocidad, ya que la recuperación no es un proceso frecuente y no suele ser largo. Se garantiza que las cosas escritas en el registro por transacciones serán recuperables si el registro está intacto.

¿Ha sido útil esta solución?