¿Cómo almacenar transmisiones interminables en un archivo?

¿Cómo almacenar transmisiones interminables en un archivo?
  • Tengamos cualquier flujo de datos binarios (como /dev/random, /dev/zeroetc.).
  • Tengamos un archivo cuyo tamaño pueda ser N como máximo.
  • Vamos a que la N está en orden de gigabytes.

Está ahícualquier elegantemétodo/tecnología-a-usar/hack en Linux, para lograr talarchivo¿La escritura continua tiene una longitud máxima de N bytes y siempre contiene solo los últimos datos escritos (secuencialmente)? Esto significa que no hay movimientos grandes (de archivo a archivo, de memoria a archivo), solo pequeños ajustes con los últimos/primeros bloques de datos.

El truco que estoy buscando es que el archivo comience a avanzar y olvide efectivamente cualquier contenido demasiado antiguo (lo que aumentaría el tamaño del archivo sobre N): rotación de contenido.

El principio deseado podría expresarse como:

inp = fopen(stream, "r");
out = fopen(file, "wN"); // "Special open" with maximal size of N

while (is_reading)
{        
    if (rd = fread(buff, block_size, 1, inp))
    {
        fwrite(buff, rd, 1, out); // Old content forgotten
        fflush(out);              // Allow others to instantly see the current content
    }
}

fclose(inp);
fclose(out);

Respuesta1

En Linux, puedes usarfallocate()para desasignar los datos al principio del archivo.

Desasignar espacio para archivos

Al especificar el indicador FALLOC_FL_PUNCH_HOLE (disponible desde Linux 2.6.38) en modo, se desasigna espacio (es decir, se crea un hueco) en el rango de bytes comenzando en el desplazamiento y continuando por len bytes. Dentro del rango especificado, los bloques parciales del sistema de archivos se ponen a cero y los bloques completos del sistema de archivos se eliminan del archivo. Después de una llamada exitosa, las lecturas posteriores de este rango devolverán ceros.

...

Eso no cambiará el tamaño del archivo informado por lso statsimilar, pero reducirá el uso real del disco a medida queel archivo será escaso. Intentar leer desde los "agujeros" del archivo tendrá éxito y devolverá 0bytes llenos al proceso de lectura.

Algo como esto:

size_t maxSize = 512UL * 1024UL * 1024UL;
char buffer[ 8 * 1024 ];
struct stat sb;
int in = open( inputFile, O_RDONLY );
int out = open( outputFile, O_WRONLY | O_CREAT | O_APPEND, 0644 );
fstat( out, &sb );
size_t fileSize = sb.st_size;

for ( ;; )
{
    ssize_t bytesRead = read( in, buffer, sizeof( buffer ) );
    if ( bytesRead < 0 )
    {
        break;
    }

    ssize_t bytesWritten = write( out, buffer, bytesRead );
    if ( bytesWritten < 0 )
    {
        break;
    }

    fileSize += bytesWritten;

    if ( fileSize > maxSize )
    {
        fsync( out );
        off_t endOfHole = fileSize - maxSize;
        fallocate( out, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
            0UL, endOfHole );
    }
}

Esto sólo es compatible con XFS, BTRFS, EXT4 y tmpfs.

También necesita mucha más verificación de errores y es posible que ni siquiera se compile tal como está. También es muy ineficiente ya que una vez que se alcanza el tamaño máximo, solicitará fallocate()cada ciclo read()/ write()y perforará el "agujero" desde el principio del archivo cada vez.

Tampoco tiene sentido utilizar fread()/ almacenado en búfer fwrite()para este patrón IO. Trozos justos read()o write()suficientemente grandes.

información relacionada