2012-03-26 22 views
8

¿Está bien que múltiples procesos accedan (escriban) al mismo archivo al mismo tiempo? Usando el siguiente código, parece funcionar, pero tengo mis dudas.Múltiples procesos accediendo al mismo archivo

El caso de uso en la instancia es un archivo ejecutable al que se llama cada vez que se recibe un correo electrónico y registra su salida en un archivo central.

if (freopen(console_logfile, "a+", stdout) == NULL || freopen(error_logfile, "a+", stderr) == NULL) { 
    perror("freopen"); 
} 
printf("Hello World!"); 

Esto se ejecuta en CentOS y compilados como C.

+0

Posible (mejor) duplicado de [fopen dos procesos] (http://stackoverflow.com/questions/1842909/fopen-two-processes). – blahdiblah

+0

Ver también [¿Se pueden agregar procesos múltiples a un archivo usando fopen sin ningún problema de simultaneidad?] (Http://stackoverflow.com/questions/7552451/can-multiple-processes-appen-to-a-file-using-fopen- sin-ninguna-concurrencia-prob). – blahdiblah

+2

No conozco el contexto del uso de tus registros, pero recomendaría echar un vistazo a 'syslog'. Tal vez te conviene. Trabajar con esto es realmente simple. http://www.gnu.org/software/libc/manual/html_node/Submitting-Syslog-Messages.html –

Respuesta

8

Utilizando el servicio IO C estándar introduce un nuevo nivel de complejidad; el archivo se modifica únicamente a través de write(2) -familia de llamadas al sistema (o asignaciones de memoria, pero eso no se usa en este caso) - los contenedores IO estándar C pueden posponer la escritura en el archivo por un tiempo y no pueden enviar solicitudes completas en un sistema llamada.

La llamada write(2) sí debe comportarse así:

[...] If the file was 
    open(2)ed with O_APPEND, the file offset is first set to the 
    end of the file before writing. The adjustment of the file 
    offset and the write operation are performed as an atomic 
    step. 

    POSIX requires that a read(2) which can be proved to occur 
    after a write() has returned returns the new data. Note that 
    not all file systems are POSIX conforming. 

lo tanto sus subyacentes write(2) llamadas se comportará correctamente.

Para las transmisiones de IO estándar C de nivel superior, también deberá ocuparse del almacenamiento en búfer. La función setvbuf(3) se puede usar para solicitar salida sin búfer, salida con búfer de línea o salida con búfer de bloque. El comportamiento predeterminado cambia de secuencia a secuencia: si la salida estándar y el error estándar se escriben en el terminal, entonces están almacenados en búfer de línea y sin búfer por defecto. De lo contrario, el almacenamiento en bloque es el predeterminado.

Es posible que desee seleccionar manualmente el buffer de línea si sus datos están orientados de forma natural a la línea, para evitar la intercalación de datos.Si sus datos son no orientados a la línea, es posible que desee utilizarlos sin búfer o dejarlos en búfer en bloques, pero lave manualmente los datos cada vez que haya acumulado una única "unidad" de salida.

Si está escribiendo más de BUFSIZ bytes a la vez, sus escrituras se pueden intercalar. La función setvbuf(3) puede ayudar a evitar el entrelazado.

Puede ser prematuro hablar de rendimiento, pero el almacenamiento en línea será más lento que el almacenamiento en bloque. Si está registrando cerca de la velocidad del disco, es posible que desee tomar otro enfoque completamente para asegurarse de que sus escrituras no estén entrelazadas.

+0

Gran consejo sobre 'setvbuf()' y sus variantes 'setbuf()', 'setbuffer()', y 'setlinebuf()'. Eran justo lo que necesitaba. Gracias, @sarnold. –

+0

Gracias por señalar que O_APPEND es necesario para garantizar el ajuste atómico del desplazamiento del archivo. Lo omití del primer proceso que abre el archivo, porque también lo crea (así que 'append' no parece apropiado ...) – RobM

1

Esta respuesta fue incorrecta. No funciona:

Así que la condición de carrera sería:

  1. proceso 1 se abre para añadir, a continuación,
  2. proceso posterior 2 se abre para añadir, a continuación,
  3. más tarde todavía 1 escrituras y se cierra, luego
  4. finalmente 2 escribe y cierra.

Me impresionaría si eso 'funcionó' porque no está claro para mí lo que debería significar. Supongo que "trabajando" significa que todos los bytes escritos por los dos procesos están en el archivo de registro? Esperaría que escriban ambos empezando en el mismo desplazamiento de bytes, por lo que uno reemplazará los otros bytes. Todo estará bien hasta e incluyendo el paso 3. y solo aparece como un problema en el paso 4, parece una prueba fácil de escribir: abrir getchar ... escribir cerrar.

¿Es crítico que puedan tener el archivo abierto simultáneamente? Una solución más obvia si la escritura es rápida, es abrir en exclusiva.

Para una comprobación rápida en su sistema, trate de:

/* write the first command line argument to a file called foo 
* stackoverflow topic 9880935 
*/ 

#include <stdio.h> 
#include <fcntl.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 

int main (int argc, const char * argv[]) { 
    if (argc <2) { 
     fprintf(stderr, "Error: need some text to write to the file Foo\n"); 
     exit(1); 
    } 

    FILE* fp = freopen("foo", "a+", stdout); 

    if (fp == NULL) { 
     perror("Error failed to open file\n"); 
     exit(1); 
    } 

    fprintf(stderr, "Press a key to continue\n"); 
    (void) getchar();  /* Yes, I really mean to ignore the character */ 

    if (printf("%s\n", argv[1]) < 0) { 
     perror("Error failed to write to file: "); 
     exit(1);   
    } 

    fclose(fp); 

    return 0; 
} 
+0

No se sobrescriben entre sí, desde 'man freopen':' a + ... Escrituras subsiguientes en el archivo siempre terminará en el final actual del archivo'. – blahdiblah

+0

@blahdiblah - tal vez me falta algo, pero ¿cómo pueden ** no ** sobrescribir en mi ejemplo? Ambos procesos se abren para anexar, pero ninguno escribe bytes en esa etapa, por lo que el archivo tiene la misma longitud para ambos abre. Entonces ambos escriben. ¿No es el archivo compensado un atributo de la fd, y no el archivo? – gbulmer

+0

Informo solo la información en la página del manual y los resultados de mis propias pruebas. No puedo hablar con los detalles de implementación subyacentes. – blahdiblah

Cuestiones relacionadas