¿El archivo adjunto es atómico en UNIX?

106

En general, ¿qué podemos dar por sentado cuando agregamos a un archivo en UNIX desde múltiples procesos? ¿Es posible perder datos (un proceso sobrescribe los cambios del otro)? ¿Es posible que los datos se estropeen? (Por ejemplo, cada proceso agrega una línea por anexo a un archivo de registro, ¿es posible que dos líneas se estropeen?) Si el anexo no es atómico en el sentido anterior, ¿cuál es la mejor manera de garantizar la exclusión mutua?

Lajos Nagy
fuente

Respuestas:

65

Se supone que una escritura cuyo tamaño es inferior a 'PIPE_BUF' es atómica. Eso debería ser de al menos 512 bytes, aunque fácilmente podría ser más grande (Linux parece tenerlo configurado en 4096).

Esto supone que está hablando de todos los componentes totalmente compatibles con POSIX. Por ejemplo, esto no es cierto en NFS.

Pero asumiendo que escribe en un archivo de registro que abrió en el modo 'O_APPEND' y mantiene sus líneas (incluida la nueva línea) en bytes de 'PIPE_BUF', debería poder tener varios escritores en un archivo de registro sin problemas de corrupción. Cualquier interrupción llegará antes o después de la escritura, no en el medio. Si desea que la integridad del archivo sobreviva a un reinicio, también deberá llamarfsync(2) después de cada escritura, pero eso es terrible para el rendimiento.

Aclaración : lea los comentarios y la respuesta de Oz Solomon . No estoy seguro de que O_APPENDse suponga que tenga ese PIPE_BUFtamaño de atomicidad. Es muy posible que sea así como se implementó Linux write(), o puede deberse a los tamaños de bloque del sistema de archivos subyacente.

freiheit
fuente
11
En sistemas de archivos cuerdos, fsync(2)ofrece tanta garantía como lo sync(2)hace, y no tiene un impacto tan grande en el rendimiento.
ephemient
4
¿Estás seguro de eso? ¿Podría proporcionar algún vínculo sobre ese comportamiento? Lo encontré confirmado si el descriptor es una tubería, pero no pude encontrar evidencia de que funcione para ningún archivo. incluidos los objetos de archivo normales que no son NFS.
Alan Franzoni
6
¿Dónde exactamente en ... / write.html? Para O_APPEND, no veo ninguna mención de PIPE_BUF, y veo la promesa de que "no se producirá ninguna operación de modificación de archivo entre el cambio del desplazamiento del archivo y la operación de escritura" , pero no estoy tan seguro de si esto significa que la operación de escritura en sí es ininterrumpido ...
akavel
6
Como señala esta respuesta , la declaración sobre PIPE_BUFen esa página solo se aplica a tuberías y FIFO, no a archivos normales.
Greg Inozemtsev
3
Con las señales que llegan, esto puede empeorar: bugzilla.kernel.org/show_bug.cgi?id=55651 . ¿Por qué esto incluso está marcado como una respuesta? PIPE_BUF no tiene nada que ver con archivos.
reducido el
35

Editar: actualizado en agosto de 2017 con los últimos resultados de Windows.

Voy a darle una respuesta con enlaces para probar el código y los resultados como autor de la propuesta Boost.AFIO, que implementa un sistema de archivos asíncrono y una biblioteca de archivos de E / S C ++.

En primer lugar, O_APPEND o el equivalente FILE_APPEND_DATA en Windows significa que los incrementos de la extensión máxima del archivo ("longitud" del archivo) son atómicos en escritores simultáneos. Esto está garantizado por POSIX, y Linux, FreeBSD, OS X y Windows lo implementan correctamente. Samba también lo implementa correctamente, NFS antes de v5 no lo hace, ya que carece de la capacidad de formato de cable para anexar atómicamente. Entonces, si abre su archivo con solo agregar, las escrituras simultáneas no se romperán entre sí en ningún sistema operativo principal menos que NFS esté involucrado.

Sin embargo, las lecturas simultáneas de anexos atómicos pueden ver escrituras desgarradas según el sistema operativo, el sistema de archivo y los indicadores con los que abrió el archivo; el incremento de la extensión máxima del archivo es atómico, pero la visibilidad de las escrituras con respecto a las lecturas puede o no ser atómico. Aquí hay un resumen rápido por banderas, sistema operativo y sistema de archivo:


No O_DIRECT / FILE_FLAG_NO_BUFFERING:

Microsoft Windows 10 con NTFS: atomicidad de actualización = 1 byte hasta 10.0.10240 inclusive, desde 10.0.14393 al menos 1Mb, probablemente infinito (*).

Linux 4.2.6 con ext4: actualización de atomicidad = 1 byte

FreeBSD 10.2 con ZFS: actualizar la atomicidad = al menos 1 Mb, probablemente infinito (*)

O_DIRECT / FILE_FLAG_NO_BUFFERING:

Microsoft Windows 10 con NTFS: actualice la atomicidad = hasta 10.0.10240 inclusive hasta 4096 bytes solo si la página está alineada, de lo contrario, 512 bytes si FILE_FLAG_WRITE_THROUGH está desactivado, si no 64 bytes. Tenga en cuenta que esta atomicidad es probablemente una característica de PCIe DMA en lugar de estar diseñada. Desde 10.0.14393, al menos 1 Mb, probablemente infinito (*).

Linux 4.2.6 con ext4: actualizar la atomicidad = al menos 1 Mb, probablemente infinito (*). Tenga en cuenta que los Linux anteriores con ext4 definitivamente no excedían los 4096 bytes, XFS ciertamente solía tener un bloqueo personalizado, pero parece que el Linux reciente finalmente lo solucionó.

FreeBSD 10.2 con ZFS: actualizar la atomicidad = al menos 1 Mb, probablemente infinito (*)


Puede ver los resultados de las pruebas empíricas sin procesar en https://github.com/ned14/afio/tree/master/programs/fs-probe . Tenga en cuenta que probamos las compensaciones rotas solo en múltiplos de 512 bytes, por lo que no puedo decir si una actualización parcial de un sector de 512 bytes se rompería durante el ciclo de lectura-modificación-escritura.

Entonces, para responder a la pregunta del OP, las escrituras O_APPEND no interferirán entre sí, pero las lecturas simultáneas a las escrituras O_APPEND probablemente verán escrituras desgarradas en Linux con ext4 a menos que O_DIRECT esté activado, por lo que sus escrituras O_APPEND necesitarían ser un tamaño de sector múltiple.


(*) "Probablemente infinito" se deriva de estas cláusulas en la especificación POSIX:

Todas las siguientes funciones serán atómicas entre sí en los efectos especificados en POSIX.1-2008 cuando operen en archivos regulares o enlaces simbólicos ... [muchas funciones] ... leer () ... escribir ( ) ... Si dos subprocesos llaman cada uno a una de estas funciones, cada llamada verá todos los efectos especificados de la otra llamada, o ninguno de ellos. [Fuente]

y

Las escrituras se pueden serializar con respecto a otras lecturas y escrituras. Si se puede probar (por cualquier medio) que se produzca una lectura () de datos de archivo después de una escritura () de los datos, debe reflejar esa escritura (), incluso si las llamadas se realizan mediante procesos diferentes. [Fuente]

pero a la inversa:

Este volumen de POSIX.1-2008 no especifica el comportamiento de escrituras simultáneas en un archivo desde múltiples procesos. Las aplicaciones deben utilizar alguna forma de control de concurrencia. [Fuente]

Puede leer más sobre el significado de estos en esta respuesta

Niall Douglas
fuente
29

Escribí un guión para probar empíricamente el tamaño máximo de anexo atómico. El script, escrito en bash, genera múltiples procesos de trabajo que escriben firmas específicas de trabajador en el mismo archivo. Luego lee el archivo en busca de firmas superpuestas o dañadas. Puede ver la fuente del script en esta publicación de blog .

El tamaño máximo real de los agregados atómicos varía no solo según el sistema operativo, sino también según el sistema de archivos.

En Linux + ext3 el tamaño es 4096 y en Windows + NTFS el tamaño es 1024. Consulte los comentarios a continuación para ver más tamaños.

Oz Solomon
fuente
¿Con qué sistema de archivos probaste en Linux? Me pregunto si tal vez se basa en tamaños de bloques del sistema de archivos.
freiheit
@freiheit Creo que en el momento en que lo probé en ext3. Si lo ejecuta en otro FS y obtiene un resultado diferente, publique un comentario.
Oz Solomon
3
@OzSolomon, utilicé su script en Debian 7.8, y solo pude obtener escrituras atómicas de hasta 1008 bytes (¿1024-16 bytes de sobrecarga?) Tanto en mi partición ext4 como en un montaje tmpfs. Cualquier cosa más allá de eso resultó en corrupción en todo momento.
Eric Pruitt
6
Su prueba parece asumir que echo $line >> $OUTPUT_FILEresultará en una sola llamada a writeindependientemente del tamaño de $line.
Tomas
16

Esto es lo que dice el estándar: http://www.opengroup.org/onlinepubs/009695399/functions/pwrite.html .

Si se establece el O_APPENDindicador de los indicadores de estado del archivo, el desplazamiento del archivo se establecerá al final del archivo antes de cada escritura y no se producirá ninguna operación de modificación del archivo entre el cambio del desplazamiento del archivo y la operación de escritura.

Bastien Léonard
fuente
20
"entre", pero ¿qué pasa con las intervenciones durante la escritura, que, según tengo entendido, ocurre después del "entre"? (Es decir: <change_offset_action> ... "the_between_period" ... <write_action>) - ¿Debo entender que no hay garantías al respecto?
akavel
@akavel estuvo de acuerdo; no hay garantía de que la escritura en sí sea atómica. Pero estoy confundido: según la garantía proporcionada en su cotización, parece que podemos concluir que una aplicación multiproceso que agrega el mismo archivo no mezclará partes de diferentes registros escritos. Sin embargo, a partir de experimentos informados por OzSolomon, vemos que incluso se viola esa suposición. ¿Por qué?
máximo
@max lo siento, me temo que no entiendo tu pregunta: en primer lugar, el experimento de OzSolomon es de múltiples procesos , no una aplicación de múltiples subprocesos (proceso único); en segundo lugar, no entiendo cómo sacas la conclusión de que "una aplicación multiproceso [...] no se mezclará" ; eso es exactamente lo que no veo garantizado por la cita de Bastien, como menciono en mi comentario. ¿Puede aclarar su pregunta?
akavel
2
Hmm, no puedo reconstruir mi propia lógica en el momento en que escribí ese comentario ... Sí, si tu interpretación es correcta, entonces, por supuesto, los diferentes registros pueden estar mezclados. Pero ahora que estoy releyendo la cita de Bastien, creo que debe significar que nadie puede interrumpir "durante la escritura"; de lo contrario, todo el párrafo en el estándar sería inútil, y literalmente no ofrece ninguna garantía (ni siquiera que la escritura ocurrirá al final, ya que alguien más podría mover el desplazamiento mientras se ejecuta el paso de "escritura".
máximo