Comprender las grabaciones de archivos simultáneas de múltiples procesos

Desde aquí: el archivo se agrega atómico en UNIX

Considere un caso donde procesos múltiples abren el mismo archivo y se agregan a él. O_APPEND garantiza que buscar al final del archivo y luego comenzar la operación de escritura es atómico. Entonces, múltiples procesos pueden agregarse al mismo archivo y ningún proceso sobrescribirá la escritura de ningún otro proceso en la medida en que cada tamaño de escritura sea <= PIPE_BUF.

Escribí un progtwig de prueba donde múltiples procesos se abren y escriben en el mismo archivo ( write(2) ). Me aseguro de que cada tamaño de escritura sea> PIPE_BUF (4k). Esperaba ver instancias en las que un proceso sobrescribiera los datos de otra persona. Pero eso no sucede. Probé con diferentes tamaños de escritura. ¿Es solo suerte o hay alguna razón por la que eso no suceda? Mi objective final es comprender si los procesos múltiples que se agregan al mismo archivo deben coordinar sus escrituras.

Aquí está el progtwig completo. Cada proceso crea un búfer int, llena todos los valores con su rank , abre un archivo y escribe en él.

Especificaciones: OpenMPI 1.4.3 en Opensuse 11.3 64-bit

Comstackdo como: mpicc -O3 test.c, ejecute como: mpirun -np 8 ./a.out

 #include  #include  #include  #include  #include  #include  #include  int main(int argc, char** argv) { int rank, size, i, bufsize = 134217728, fd, status = 0, bytes_written, tmp_bytes_written; int* buf; char* filename = "/tmp/testfile.out"; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); buf = (int*) malloc (bufsize * sizeof(int)); if(buf == NULL) { status = -1; perror("Could not malloc"); goto finalize; } for(i=0; i<bufsize; i++) buf[i] = rank; if(-1 == (fd = open(filename, O_APPEND|O_WRONLY, S_IWUSR))) { perror("Cant open file"); status = -1; goto end; exit(-1); } bytes_written = 0; if(bufsize != (tmp_bytes_written = write(fd, buf, bufsize))) { perror("Error during write"); printf("ret value: %d\n", tmp_bytes_written); status = -1; goto close; } close: if(-1 == close(fd)) { perror("Error during close"); status = -1; } end: free(buf); finalize: MPI_Finalize(); return status; } 

La atomicidad de escribe menos que PIPE_BUF aplica solo a tuberías y FIFO. Para las escrituras de archivos, POSIX dice:

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

… lo que significa que estás solo: diferentes me gusta de UNIX te darán diferentes garantías.

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 los escritores simultáneos, y eso es por cualquier cantidad, no solo PIPE_BUF. 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, ya que carece de la capacidad de formato de cable para anexarse ​​atómicamente. Por lo tanto, si abre su archivo con solo anexar, las escrituras concurrentes no se romperán entre sí en ningún sistema operativo importante a menos que se trate de NFS.

Sin embargo, esto no dice nada sobre si las lecturas verán alguna vez una escritura fragmentada, y en eso POSIX dice lo siguiente acerca de la atomicidad de read () y write () en los archivos normales:

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

y

Las escrituras se pueden serializar con respecto a otras lecturas y escrituras. Si puede demostrarse (por algún medio) que una lectura () de datos de archivo ocurre después de una escritura () de los datos, debe reflejar dicha 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 las escrituras simultáneas en un archivo de múltiples procesos. Las aplicaciones deben usar alguna forma de control de concurrencia. [Fuente]

Una interpretación segura de estos tres requisitos sugeriría que todas las escrituras superpuestas en una extensión en el mismo archivo deben ser serializadas una con respecto a la otra y que las escrituras desgarradas nunca aparezcan para los lectores.

Una interpretación menos segura, pero aún permitida, podría ser que las lecturas y escrituras solo se serializan entre hilos dentro del mismo proceso, y entre procesos las escrituras se serializan con respecto a las lecturas solamente (es decir, hay orden de E / S secuencialmente consistente entre hilos en un proceso, pero entre procesos i / o es solo adquirir-liberar).

Por supuesto, el hecho de que el estándar requiera esta semántica no significa que las implementaciones cumplan, aunque de hecho FreeBSD con ZFS se comporta a la perfección, Windows muy reciente (10.0.14393) con NTFS se comporta perfectamente y Linuxes recientes con ext4 se comporta correctamente si O_DIRECT está activado . Si desea más detalles sobre qué tan bien cumplen los sistemas operativos y de archivos principales con el estándar, vea esta respuesta

No es suerte, en el sentido de que si profundizas en el núcleo probablemente puedas probar que en tus circunstancias particulares nunca sucederá que la write un proceso esté intercalada con otra. Estoy asumiendo que:

  • No estás alcanzando ningún límite de tamaño de archivo
  • No está llenando el sistema de archivos en el que crea el archivo de prueba
  • El archivo es un archivo regular (no un socket, pipe u otra cosa)
  • El sistema de archivos es local
  • El búfer no abarca múltiples asignaciones de memoria virtual (se sabe que esta es verdadera, porque es malloc() ed, que lo coloca en el montón, que es contiguo.
  • Los procesos no son interrumpidos, señalados o rastreados mientras write() está ocupado.
  • No hay errores de E / S de disco, fallas de RAM ni ninguna otra condición anormal.
  • (Tal vez otros)

Probablemente descubrirá que si todas esas suposiciones se cumplen, es el caso de que el kernel del sistema operativo que está utilizando siempre logre una única llamada al sistema write() con una sola escritura contigua atómica en el siguiente archivo.

Eso no significa que puedas contar con que esto siempre es verdad. Nunca se sabe cuándo podría no ser cierto cuando:

  • el progtwig se ejecuta en un sistema operativo diferente
  • el archivo se mueve a un sistema de archivos NFS
  • el proceso recibe una señal mientras write() está en progreso y write() devuelve un resultado parcial (menos bytes que los solicitados). No estoy seguro si POSIX realmente permite que esto suceda, ¡pero programo a la defensiva!
  • etc …

Por lo tanto, su experimento no puede demostrar que puede hacerlo en escrituras no intercaladas.