Wie speichere ich einen endlosen Stream in einer Datei?

Wie speichere ich einen endlosen Stream in einer Datei?
  • Lassen Sie uns einen beliebigen Strom binärer Daten haben (wie /dev/random, /dev/zero, usw.).
  • Nehmen wir eine Datei an, deren Größe maximal N betragen kann.
  • Nehmen wir an, N ist in der Größenordnung von Gigabyte.

Ist dajede eleganteMethode/Technologie-zu-verwenden/Hack in Linux, um solche zu erreichenDateikontinuierlich geschriebene Daten maximal N Bytes lang sind und immer nur die zuletzt geschriebenen Daten enthalten (sequenziell)? Das bedeutet keine großen Verschiebungen (Datei zu Datei, Speicher zu Datei), sondern nur kleine Optimierungen an den letzten/ersten Datenblöcken.

Der Trick, nach dem ich suche, besteht darin, dass die Datei am Anfang nach vorne verschoben wird und zu alte Inhalte (die die Dateigröße über N erhöhen würden) effektiv vergessen werden – Inhaltsrotation.

Das gewünschte Prinzip könnte wie folgt ausgedrückt werden:

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);

Antwort1

Unter Linux können Siefallocate()um die Daten am Anfang der Datei freizugeben.

Dateispeicherplatz freigeben

Die Angabe des Flags FALLOC_FL_PUNCH_HOLE (verfügbar seit Linux 2.6.38) im Modus gibt Speicherplatz frei (d. h. es wird ein Loch erstellt) im Bytebereich, beginnend bei Offset und fortgesetzt für len Bytes. Innerhalb des angegebenen Bereichs werden teilweise Dateisystemblöcke auf Null gesetzt und ganze Dateisystemblöcke aus der Datei entfernt. Nach einem erfolgreichen Aufruf geben nachfolgende Lesevorgänge aus diesem Bereich Nullen zurück.

...

lsDies ändert nicht die von oder oder ähnlichem gemeldete Dateigröße stat, verringert jedoch die tatsächliche Festplattennutzung, da dieDatei wird spärlich sein. Der Versuch, aus den „Löchern“ in der Datei zu lesen, ist dennoch erfolgreich und gibt mit 0- aufgefüllte Bytes an den Lesevorgang zurück.

Etwas wie das:

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 );
    }
}

Dies wird nur auf XFS, BTRFS, EXT4 und tmpfs unterstützt.

Es erfordert außerdem viel mehr Fehlerprüfungen und kann möglicherweise nicht einmal so kompiliert werden. Es ist außerdem sehr ineffizient, da es, sobald die maximale Größe erreicht ist, fallocate()für jeden read()/ write()-Zyklus aufgerufen wird und jedes Mal das „Loch“ vom Anfang der Datei stanzt.

fread()Es macht auch keinen Sinn, für dieses IO-Muster gepufferte / zu verwenden fwrite(). Nur read()/ write()ausreichend große Blöcke.

verwandte Informationen