Ich möchte eine Datei von A nach B kopieren, die sich möglicherweise auf verschiedenen Dateisystemen befindet.
Es gibt einige zusätzliche Anforderungen:
- Die Kopie ist alles oder nichts, keine teilweise oder beschädigte Datei B bleibt beim Absturz an Ort und Stelle;
- Überschreiben Sie keine vorhandene Datei B;
- Konkurrieren Sie nicht mit einer gleichzeitigen Ausführung desselben Befehls, höchstens kann dies gelingen.
Ich denke das kommt näher:
cp A B.part && \
ln B B.part && \
rm B.part
Aber 3. wird verletzt, indem der cp nicht fehlschlägt, wenn B.part existiert (auch mit -n Flag). Anschließend könnte 1. fehlschlagen, wenn der andere Prozess den CP "gewinnt" und die verknüpfte Datei unvollständig ist. B.part könnte auch eine nicht verwandte Datei sein, aber ich bin froh, dass ich scheitern kann, ohne in diesem Fall andere versteckte Namen auszuprobieren.
Ich denke, Bash Noclobber hilft, funktioniert das voll und ganz? Gibt es eine Möglichkeit, ohne die Bash-Versionsanforderung auszukommen?
#!/usr/bin/env bash
set -o noclobber
cat A > B.part && \
ln B.part B && \
rm B.part
Follow-up, ich weiß, dass einige Dateisysteme dabei sowieso ausfallen werden (NFS). Gibt es eine Möglichkeit, solche Dateisysteme zu erkennen?
Einige andere verwandte, aber nicht ganz dieselben Fragen:
Annäherung der atomaren Bewegung über Dateisysteme hinweg?
https://rcrowley.org/2010/01/06/things-unix-can-do-atomically.html
mv
überschreibt eine vorhandene Datei. B.mv -n
benachrichtigt nicht, dass ein Fehler aufgetreten ist.ln(1)
(rename(2)
) schlägt fehl, wenn B bereits vorhanden ist.Antworten:
rsync
macht diesen Job. Eine temporäre Datei wirdO_EXCL
standardmäßig erstellt (nur deaktiviert, wenn Sie sie verwenden--inplace
) und dannrenamed
über der Zieldatei. Verwenden Sie--ignore-existing
diese Option, um B nicht zu überschreiben, falls vorhanden.In der Praxis hatte ich auf ext4-, zfs- oder sogar NFS-Mounts nie Probleme damit.
quelle
Keine Sorge,
noclobber
ist eine Standardfunktion .quelle
Sie haben nach NFS gefragt. Diese Art von Code wird wahrscheinlich unter NFS beschädigt, da die Prüfung auf
noclobber
zwei separate NFS-Vorgänge umfasst (prüfen, ob eine Datei vorhanden ist, neue Datei erstellen) und zwei Prozesse von zwei separaten NFS-Clients möglicherweise in einen Race-Zustand geraten, in dem beide erfolgreich sind ( beide verifizieren, dass esB.part
noch nicht existiert, und beide erstellen es erfolgreich, wodurch sie sich gegenseitig überschreiben.)Es gibt nicht wirklich eine generische Überprüfung, ob das Dateisystem, in das Sie schreiben, so etwas wie
noclobber
atomar unterstützt oder nicht. Sie könnten den Dateisystemtyp überprüfen, ob es sich um NFS handelt, aber das wäre eine Heuristik und nicht unbedingt eine Garantie. Dateisysteme wie SMB / CIFS (Samba) leiden wahrscheinlich unter denselben Problemen. Dateisysteme, die über FUSE verfügbar gemacht werden, können sich möglicherweise korrekt verhalten oder nicht, dies hängt jedoch hauptsächlich von der Implementierung ab.Ein möglicherweise besserer Ansatz besteht darin, die Kollision im
B.part
Schritt zu vermeiden , indem Sie einen eindeutigen Dateinamen verwenden (in Zusammenarbeit mit anderen Agenten), damit Sie sich nicht darauf verlassen müssennoclobber
. Beispielsweise können Sie als Teil des Dateinamens Ihren Hostnamen, Ihre PID und einen Zeitstempel (+ möglicherweise eine Zufallszahl) angeben. Da auf einem Host zu einem bestimmten Zeitpunkt ein einzelner Prozess unter einer bestimmten PID ausgeführt werden sollte, sollte dies der Fall sein Einzigartigkeit garantieren.Also eines von:
Oder:
Wenn Sie also eine Race-Bedingung zwischen zwei Agenten haben, fahren beide mit der Operation fort, aber die letzte Operation ist atomar, sodass entweder B mit einer vollständigen Kopie von A existiert oder B nicht existiert.
Sie können die Größe des Rennens reduzieren, indem Sie nach der Kopie und vor der Operation
mv
oder erneut prüfenln
, aber es gibt immer noch eine kleine Rennbedingung. Unabhängig von der Rennbedingung sollte der Inhalt von B jedoch konsistent sein, vorausgesetzt, beide Prozesse versuchen, ihn aus A (oder einer Kopie aus einer gültigen Datei als Ursprung) zu erstellen.Beachten Sie, dass in der ersten Situation mit
mv
, wenn ein Rennen existiert, der letzte Prozess derjenige ist, der gewinnt, da das Umbenennen (2) eine vorhandene Datei atomar ersetzt:Es ist also durchaus möglich, dass Prozesse, die B zu diesem Zeitpunkt verbrauchen, während dieses Prozesses unterschiedliche Versionen davon (unterschiedliche Inodes) sehen. Wenn die Autoren nur alle versuchen, denselben Inhalt zu kopieren, und die Leser einfach den Inhalt der Datei verbrauchen, ist dies möglicherweise in Ordnung. Wenn sie unterschiedliche Inodes für Dateien mit demselben Inhalt erhalten, sind sie trotzdem glücklich.
Der zweite Ansatz, bei dem ein Hardlink verwendet wird, sieht besser aus, aber ich erinnere mich, dass ich von vielen gleichzeitigen Clients Experimente mit Hardlinks in einer engen Schleife auf NFS durchgeführt und den Erfolg gezählt habe. Dort schien es immer noch einige Rennbedingungen zu geben, unter denen zwei Clients einen Hardlink herausgaben Operation zur gleichen Zeit, mit dem gleichen Ziel, schienen beide erfolgreich zu sein. (Es ist möglich, dass dieses Verhalten mit der jeweiligen NFS-Server-Implementierung YMMV zusammenhängt.) In jedem Fall ist dies wahrscheinlich die gleiche Art von Race-Bedingung, bei der Sie möglicherweise zwei separate Inodes für dieselbe Datei erhalten, wenn es schwer ist Parallelität zwischen Autoren, um diese Rennbedingungen auszulösen. Wenn Ihre Autoren konsistent sind (beide kopieren A nach B) und Ihre Leser nur den Inhalt konsumieren, könnte dies ausreichen.
Schließlich haben Sie das Sperren erwähnt. Leider fehlt das Sperren stark, zumindest in NFSv3 (nicht sicher über NFSv4, aber ich wette, es ist auch nicht gut). Wenn Sie das Sperren in Betracht ziehen, sollten Sie verschiedene Protokolle für das verteilte Sperren prüfen, möglicherweise außerhalb des Bandes mit dem tatsächliche Dateikopien, aber das ist sowohl störend als auch komplex und anfällig für Probleme wie Deadlocks. Daher würde ich sagen, dass es besser ist, dies zu vermeiden.
Weitere Informationen zum Thema Atomizität in NFS finden Sie im Maildir-Postfachformat , das erstellt wurde, um Sperren zu vermeiden und auch unter NFS zuverlässig zu funktionieren. Dies geschieht, indem überall eindeutige Dateinamen beibehalten werden (sodass Sie am Ende nicht einmal ein endgültiges B erhalten).
Vielleicht etwas interessanter für Ihren speziellen Fall, erweitert das Maildir ++ - Format Maildir, um Unterstützung für das Postfachkontingent hinzuzufügen, und zwar durch atomares Aktualisieren einer Datei mit einem festen Namen innerhalb des Postfachs (so dass dies möglicherweise näher an Ihrem B. liegt). Ich denke, Maildir ++ versucht es Anhängen, was unter NFS nicht wirklich sicher ist, aber es gibt einen Neuberechnungsansatz, der ein ähnliches Verfahren verwendet und als atomarer Ersatz gültig ist.
Hoffentlich sind all diese Hinweise nützlich!
quelle
Sie können dafür ein Programm schreiben.
Verwenden Sie
open(O_CREAT|O_RDWD)
diese Option , um die Zieldatei zu öffnen, alle Bytes und Metadaten zu lesen und zu überprüfen, ob die Zieldatei vollständig ist. Wenn nicht, gibt es zwei Möglichkeiten:Unvollständiges Schreiben
Ein anderer Prozess führt dasselbe Programm aus.
Versuchen Sie, eine geöffnete Dateibeschreibungssperre für die Zieldatei zu erhalten.
Ein Fehler bedeutet, dass gleichzeitig ein Prozess stattfindet. Der aktuelle Prozess sollte vorhanden sein.
Erfolg bedeutet, dass der letzte Schreibvorgang abgestürzt ist. Sie sollten von vorne beginnen oder versuchen, ihn durch Schreiben in die Datei zu beheben.
Beachten Sie auch, dass Sie
fsync()
nach dem Schreiben in die Zieldatei besser sind, bevor Sie die Datei schließen und die Sperre aufheben. Andernfalls werden möglicherweise noch nicht auf der Festplatte befindliche Daten gelesen.https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html
Dies ist wichtig, damit Sie zwischen einem gleichzeitig ausgeführten Programm und einem zuletzt abgestürzten Vorgang unterscheiden können.
quelle
Sie erhalten das richtige Ergebnis, wenn Sie
cp
zusammen mitmv
. Dies ersetzt entweder "B" durch eine neue Kopie von "A" oder lässt "B" wie zuvor.Update zur Anpassung an vorhandene
B
:Dies ist nicht 100% atomar, aber es kommt nahe. Es gibt eine Rennbedingung, bei der zwei dieser Dinge ausgeführt werden, beide gleichzeitig in den
if
Test eintreten , beide sehen, dassB
dies nicht vorhanden ist, und dann beide den Test ausführenmv
.quelle
mv B.tmp B
wird erst ausgeführt, wenn escp A B.tmp
zuerst ausgeführt wird und einen Erfolgsergebniscode zurückgibt. Wie ist das ein Fehler? Ich bin auch damit einverstanden, dasscp A B.tmp
ein vorhandenes überschriebenB.tmp
wird, was Sie tun möchten. Das&&
garantiert, dass der 2. Befehl genau dann ausgeführt wird, wenn der erste normal ausgeführt wird.