Frage

Frage

Frage

Angenommen, ich habe ein Nicht-Verzeichnis (Datei, benannte Pipe/Socket, was auch immer) unter dem Pfadnamen /tmp/foound ein anderes Nicht-Verzeichnis unter dem Pfadnamen /tmp/bar. Dann werden zwei (oder mehr) Prozesse gleichzeitig ausgeführt:

Der erste Vorgang:

unlink('/tmp/foo') /* or rename('/tmp/foo', '/tmp/removed') */
unlink('/tmp/bar') /* or rename('/tmp/bar', '/tmp/removed') */

Prozess zwei (und so weiter) führt Folgendes aus:

link('/tmp/foo', '/tmp/bar')

So wie ich das verstehe, kann Prozess zwei auf keinen Fall erfolgreich sein (entweder link(2)wird versucht, während /tmp/foonoch vorhanden ist, wobei in diesem Fall /tmp/barauch vorhanden ist, sodass es mit fehlschlagen muss EEXIST, oder /tmp/fooes ist weg, sodass es mit fehlschlagen muss ENOENT).

Diese Intuition basiert jedoch auf der Annahme, dass die unlink(2)und/oder rename(2)Systemaufrufe in ihrer Aufhebungswirkung von Natur aus sequentiell sind. Daher suche ich nach einer Bestätigung meines Verständnisses: Gibt es da draußen irgendein *nix-ähnliches System, dessen Kernel den Erfolg der beiden unlink(2)und/oder rename(2)Aufrufe zulässt, aber gleichzeitig link(2)auch dafür sorgt, dass diese erfolgreich sind (sei es durch Neuanordnung der Aufhebung der Verknüpfung von /tmp/foound /tmp/barund ohne Abstraktion/Verbergen dieser vor dem aufrufenden Prozess link(2)oder durch einen anderen skurrilen Race Condition/Bug)?

Aktuelles Verständnis

Ich habe die Manpages für unlink(2), rename(2), und link(2)für Linux und einige BSDs sowie die POSIX-Spezifikation für diese Funktionen gelesen. Aber ich glaube nicht, dass sie bei genauerer Betrachtung tatsächlich etwas Beruhigendes zu diesem Thema enthalten. Zumindest bei rename(2)wird uns versprochen, dass dieZielwird atomar ersetzt, wenn es bereits vorhanden ist (Fehler im Betriebssystem selbst beiseite), aber nichts anderes.

Ich habe gesehenAnsprüchedass mehrere gleichzeitige Ausführungen von rename(foo, qux)atomar und portabel dazu führen, dass alle Umbenennungen bis auf eine fehlschlagen – das ist also vielversprechend! Ich bin mir nur nicht sicher, ob das auch auf ein Fehlschlagen von unter denselben Umständen ENOENTausgeweitet werden kann .link(foo, bar)ENOENT

Bevorzugte Antworten

Mir ist bewusst, dass dies eine dieser „Negativsituationen“ ist. Wir können bestenfalls feststellen, dass es keinen Beweis dafür gibt, dass ein *nix-ähnliches System link(2)existiert, das den Erfolg von Prozess zwei ermöglicht.

Ich suche also nach Antworten, die so viele *nix-ähnliche Systeme wie möglich abdecken (zumindest Linux, OS X und die verschiedenen BSDs, aber idealerweise auch die proprietären Systeme, die teilweise noch im Einsatz sind, wie Solaris 10) – von Leuten, die mit diesen Systemen und diesem engen Problembereich (atomische/wohlgeordnete Dateisystemoperationen) ausreichend vertraut sind, sodass sie zuversichtlich sind (soweit man das realistischerweise sein kann), dass sie Probleme wie den oben erwähnten Mac OS X- rename(2)Fehler kennen würden, wenn sie auf den Plattformen existieren würden, mit denen sie vertraut sind. Das würde mir genug Vertrauen geben, dass dies so funktioniert, wie ich denke, und zwar auf eine portierbare Weise, auf die man sich verlassen kann.

Schlussbemerkung

Dies ist keine Frage zum „X/Y-Problem“ – es gibt kein zugrunde liegendes Problem, das durch einen Verweis auf die verschiedenen Sperr-/IPC-Mechanismen oder etwas anderes beantwortet werden könnte, das die Unsicherheit hinsichtlich der Interaktion dieser bestimmten Systemaufrufe umgeht. Insbesondere möchte ich wissen, ob man sich darauf verlassen kann, dass die oben genannten Systemaufrufe zwischen *nix-ähnlichen Systemen, die heute in der Praxis verwendet werden, wie erwartet portabel interagieren.

Antwort1

Betrachten Sie Standards wiePOSIXfür Portabilitätsgarantien. In der Praxis weisen die meisten POSIX-kompatiblen Systeme geringfügige Abweichungen von den Spezifikationen auf, aber im Allgemeinen können Sie sich auf die in der Spezifikation gegebenen Garantien verlassen. Die meisten modernen Unix-Systeme entsprechen der Spezifikation, auch wenn sie nicht formal getestet wurden. Sie müssen möglicherweise in einem POSIX-Modus ausgeführt werden, z. B. durch Einstellen POSIXLY_CORRECT=1mit Bash oder durch Sicherstellen, dass dies /usr/xpg4/binvor /binund /usr/binin PATHSolaris liegt.

Single Unix v2(eine ältere Erweiterung von POSIX) hat folgendes zu sagen überlink:

Die link()Funktion erstellt automatisch einen neuen Link für die vorhandene Datei und die Linkanzahl der Datei wird um eins erhöht.

Umrename:

Wenn der Link mit dem NamenneuArgument vorhanden ist, wird es entfernt undaltumbenannt inneu. In diesem Fall bleibt ein Link mit dem Namen new während der gesamten Umbenennungsoperation für andere Prozesse sichtbar und verweist entweder auf die Datei, auf die inneuoderaltbevor die Operation begann.

POSIX legt ausdrücklich fest, dass, wenn das Ziel existiert, sein Ersatz atomar sein muss. Es legt jedoch nicht fest, dass die Umbenennung selbst atomar sein muss, d. h. dass es keinen Zeitpunkt gibt, an dem beidealtUndneuauf die betreffende Datei verweisen oder wenn beides nicht der Fall ist. In der Praxis sind diese Eigenschaften auf Unix-Systemen wahr, zumindest bei lokalen Dateisystemen.

Darüber hinaus ist die Reihenfolge der Operationen garantiert: In C ;garantiert /newline die sequentielle Ausführung; in sh ;garantiert /newline die sequentielle Ausführung (wie do &&usw.); andere Programmiersprachen bieten ähnliche Garantien. In

unlink("/tmp/foo");
unlink("/tmp/bar");

Es ist garantiert, dass es keinen Zeitpunkt gibt, an dem /tmp/fooexistiert, aber nicht /tmp/bar(vorausgesetzt, dass /tmp/bares anfangs existiert). Daher kann die Ausführung eines gleichzeitig ausgeführten Prozesses link("/tmp/foo", "/tmp/bar")nicht erfolgreich sein.

Beachten Sie, dass Atomizität nicht garantiertWiderstandsfähigkeit. Bei Atomarität geht es um beobachtbares Verhalten auf einem Live-System. Resilienz im Zusammenhang mit Dateisystemen dreht sich darum, was im Falle eines Systemabsturzes passiert. Viele Dateisysteme opfern Resilienz zugunsten der Leistung. Wenn also die Ausführung von unlink("foo"); unlink("bar");unterbrochen wird (mit dem aktuellen Verzeichnis auf dem Festplattenspeicher), ist es möglich, dass bargelöscht wird und foozurückbleibt.

Einige Netzwerkdateisysteme bieten weniger Garantien, wenn Vorgänge auf verschiedenen Clients stattfinden. Ältere Implementierungen von NFS waren dafür berüchtigt. Ich denke, moderne Implementierungen sind besser, aber ich habe keine Erfahrung mit modernem NFS.

verwandte Informationen