Tengo un caso en el que hay dos procesos que actúan en el mismo archivo: uno como escritor y otro como lector. El archivo es un archivo de texto de una línea y el escritor vuelve a escribir la línea en un bucle. el lector lee la línea. El pseudocódigo se ve así:
Proceso de escritor
char buf[][18] = { "xxxxxxxxxxxxxxxx", "yyyyyyyyyyyyyyyy" }; i = 0; while (1) { pwrite(fd, buf[i], 18, 0); i = (i + 1) % 2; }Proceso del lector
while(1) { pread(fd, readbuf, 18, 0); //check if readbuf is either buf[0] or buf[1] } Después de un tiempo de ejecutar ambos procesos, pude ver que el readbuf es xxxxxxxxxxxxxxxxyy o yyyyyyyyyyyyyyyyxx .
Según entendí, la escritura sería atómica para tamaños de hasta 512 bytes. pero según mi experimento, parece que la atomicidad es solo para 16 bytes.
Las páginas del manual no dicen nada sobre la atomicidad de los archivos normales, solo mencionan la atomicidad de la tubería para 512 bytes.
He probado esto con tmpfs y ext4 y el resultado es el mismo. con O_SYNC , las escrituras ext4 se vuelven atómicas y lo entiendo porque las escrituras no regresan hasta que llegan al disco, pero O_SYNC no ayuda para tmpfs ( /dev/shm ).
POSIX no brinda ninguna garantía mínima de operaciones atómicas para read ywrite , excepto para escrituras en una tubería (donde se garantiza que una escritura de hasta PIPE_BUF (≥ 512) bytes sea atómica, pero las lecturas no tienen garantía de atomicidad). La operación de read y write se describe en términos de valores de bytes; aparte de las canalizaciones, una operación de write no ofrece garantías adicionales en comparación con un bucle alrededor de las operaciones de write de un solo byte.
No estoy al tanto de ninguna garantía adicional que daría Linux, ni 16 ni 512. En la práctica, esperaría que dependiera de la versión del kernel, del sistema de archivos y posiblemente de otros factores, como el dispositivo de bloque subyacente, el número de CPU, la arquitectura de la CPU, etc.
Las O_SYNC , O_RSYNC y O_DSYNC ( compleción de integridad de datos de E/S sincronizada , otorgada para read y write en la función SIO opcional de POSIX) no son lo que necesita. Garantizan que las escrituras se comprometen al almacenamiento persistente antes de la llamada al sistema de read o write , pero no hacen ningún reclamo con respecto a una write que se inicia mientras la operación de read está en curso.
En su escenario, leer y escribir archivos no parece el conjunto de herramientas adecuado.
mmap si lo es. Esto no resuelve mágicamente el problema de la atomicidad, pero es probable que mejore el rendimiento de un mecanismo de sincronización adecuado. Para realizar la sincronización, existen dos enfoques básicos:mmap + msync ) o un canal diferente (por ejemplo, tubería).msync ). Luego, el productor escribe un valor bien conocido en una palabra de la máquina (un sig_atomic_t normalmente funcionará, aunque su atomicidad está formalmente garantizada solo para señales o, en la práctica, un uintptr_t ). El consumidor lee esa palabra de máquina y solo procesa los datos correspondientes si esta palabra tiene un valor aceptable.El requisito de atomicidad PIPE_BUF se aplica a tuberías y FIFO. POSIX otorga a los archivos normales un requisito de atomicidad diferente, pero el kernel de Linux no se ajusta. El requisito de atomicidad de archivos regulares aparece en 2.9.7 Interacciones de subprocesos con operaciones de archivos regulares . Cada vez que una implementación de escritura () devuelve algún valor positivo N, toda la escritura de N bytes será atómica. (Una implementación de write() conforme podría optar por devolver siempre un valor menor o igual a uno, aceptando solo un byte a la vez, en cuyo caso la atomicidad no tiene ningún beneficio práctico).
Si bien algunos han argumentado públicamente que la atomicidad de un archivo regular se aplica solo a los subprocesos que comparten un proceso, no hay precedentes de que POSIX escriba "dos subprocesos" cuando significa "dos subprocesos del mismo proceso". Además, la parte sobre "también se aplicará siempre que un descriptor de archivo se cierre con éxito, sin importar la causa (por ejemplo, [...] la terminación del proceso)" sería superflua en un requisito aislado a los subprocesos de un proceso.