Ein Dateisperre angebracht ist, um eine Datei durch eine Dateibeschreibung . Auf hoher Ebene lautet die Abfolge der Vorgänge in einer Instanz des Skripts wie folgt:
- Öffnen Sie die Datei, an die die Sperre angehängt ist ("die Sperrdatei").
- Machen Sie eine Sperre für die Sperrdatei.
- Sachen machen.
- Schließen Sie die Sperrdatei. Dadurch wird die Sperre aufgehoben, die an die Dateibeschreibung angehängt ist, die durch Öffnen einer Datei erstellt wurde.
Wenn Sie die Sperre gedrückt halten, wird verhindert, dass eine weitere Kopie desselben Skripts ausgeführt wird, da Sperren dies tun. Solange eine exklusive Sperre für eine Datei irgendwo im System vorhanden ist, ist es unmöglich, eine zweite Instanz derselben Sperre zu erstellen, selbst wenn eine andere Dateibeschreibung verwendet wird.
Beim Öffnen einer Datei wird eine Dateibeschreibung erstellt . Dies ist ein Kernel-Objekt, das in Programmierschnittstellen nicht direkt sichtbar ist. Sie greifen indirekt über Dateideskriptoren auf eine Dateibeschreibung zu, normalerweise stellen Sie sich den Zugriff auf die Datei vor (Lesen oder Schreiben des Inhalts oder der Metadaten). Eine Sperre ist eines der Attribute, die eine Eigenschaft für die Dateibeschreibung und nicht für eine Datei oder einen Deskriptor sind.
Zu Beginn des Öffnens einer Datei verfügt die Dateibeschreibung über einen einzelnen Dateideskriptor. Sie können jedoch weitere Deskriptoren erstellen, indem Sie entweder einen anderen Deskriptor (die dup
Familie der Systemaufrufe) erstellen oder einen Unterprozess forken (nach dem sowohl der übergeordnete als auch der übergeordnete Deskriptor ) Kind hat Zugriff auf die gleiche Dateibeschreibung). Ein Dateideskriptor kann explizit geschlossen werden oder wenn der Prozess, in dem er sich befindet, abstürzt. Wenn der letzte an eine Datei angehängte Dateideskriptor geschlossen wird, wird die Dateibeschreibung geschlossen.
So wirkt sich die oben beschriebene Abfolge von Vorgängen auf die Dateibeschreibung aus.
- Die Umleitung
<$0
öffnet die Skriptdatei in der Subshell und erstellt eine Dateibeschreibung. An dieser Stelle befindet sich ein einzelner Dateideskriptor, der an die Beschreibung angehängt ist: Deskriptor Nummer 0 in der Subshell.
- Die Subshell wird aufgerufen
flock
und wartet darauf, dass sie beendet wird. Während Flock ausgeführt wird, sind zwei Deskriptoren an die Beschreibung angehängt: Nummer 0 in der Subshell und Nummer 0 im Flock-Prozess. Wenn Flock die Sperre übernimmt, wird eine Eigenschaft der Dateibeschreibung festgelegt. Wenn eine andere Dateibeschreibung bereits eine Sperre für die Datei hat, kann Flock die Sperre nicht übernehmen, da es sich um eine exklusive Sperre handelt.
- Die Subshell macht Sachen. Da für die Beschreibung mit der Sperre immer noch ein offener Dateideskriptor vorhanden ist, bleibt diese Beschreibung bestehen und die Sperre bleibt bestehen, da niemand die Sperre aufhebt.
- Die Unterschale stirbt in der schließenden Klammer. Dadurch wird der letzte Dateideskriptor in der Dateibeschreibung mit der Sperre geschlossen, sodass die Sperre an dieser Stelle verschwindet.
Der Grund, warum das Skript eine Umleitung von verwendet, $0
ist, dass die Umleitung die einzige Möglichkeit ist, eine Datei in der Shell zu öffnen, und dass eine Umleitung aktiv bleibt, die einzige Möglichkeit, einen Dateideskriptor offen zu halten. Die Subshell liest nie von ihrer Standardeingabe, sondern muss nur geöffnet bleiben. In einer Sprache, die direkten Zugriff auf das Öffnen und Schließen von Anrufen bietet, können Sie verwenden
fd = open($0)
flock(fd, LOCK_EX)
do stuff
close(fd)
Sie können die gleiche Abfolge von Operationen in der Shell erhalten, wenn Sie die Umleitung mit dem exec
eingebauten Befehl ausführen.
exec <$0
flock -n -x 0
# do stuff
exec <&-
Das Skript könnte einen anderen Dateideskriptor verwenden, wenn weiterhin auf die ursprüngliche Standardeingabe zugegriffen werden soll.
exec 3<$0
flock -n -x 0
# do stuff
exec 3<&-
oder mit einer Unterschale:
(
flock -n -x 3
# do stuff
) 3<$0
Die Sperre muss sich nicht in der Skriptdatei befinden. Es kann sich um eine beliebige Datei handeln, die zum Lesen geöffnet werden kann (es muss also vorhanden sein, es muss sich um einen Dateityp handeln, der gelesen werden kann, z. B. eine reguläre Datei oder eine Named Pipe, jedoch kein Verzeichnis, und der Skriptprozess muss über Folgendes verfügen die Erlaubnis, es zu lesen). Die Skriptdatei hat den Vorteil, dass sie garantiert vorhanden und lesbar ist (außer in dem Randfall, in dem sie zwischen dem Aufrufen des Skripts und dem Erreichen der <$0
Umleitung durch das Skript extern gelöscht wurde ).
Solange dies flock
erfolgreich ist und sich das Skript in einem Dateisystem befindet, in dem Sperren nicht fehlerhaft sind (einige Netzwerkdateisysteme wie NFS sind möglicherweise fehlerhaft), verstehe ich nicht, wie die Verwendung einer anderen Sperrdatei eine Race Condition ermöglichen kann. Ich vermute einen Manipulationsfehler von Ihrer Seite.