Am 19. August 2013, Randal L. Schwartz geschrieben diesen Shell - Skript, das auf Linux , um sicherzustellen , sollte, „dass nur eine Instanz von [dem] Skript ausgeführt wird , ohne Rennbedingungen oder mit Lock - Dateien zu bereinigen“:
#!/bin/sh
# randal_l_schwartz_001.sh
(
if ! flock -n -x 0
then
echo "$$ cannot get flock"
exit 0
fi
echo "$$ start"
sleep 10 # for testing. put the real task here
echo "$$ end"
) < $0
Es scheint wie angekündigt zu funktionieren:
$ ./randal_l_schwartz_001.sh & ./randal_l_schwartz_001.sh
[1] 11863
11863 start
11864 cannot get flock
$ 11863 end
[1]+ Done ./randal_l_schwartz_001.sh
$
Folgendes verstehe ich:
- Das Skript leitet (
<
) eine Kopie seines eigenen Inhalts (dh von$0
) an die STDIN (dh den Dateideskriptor0
) einer Subshell weiter. - Innerhalb der Subshell versucht das Skript, eine nicht blockierende exklusive Sperre (
flock -n -x
) für den Dateideskriptor abzurufen0
.- Wenn dieser Versuch fehlschlägt, wird die Subshell beendet (und das Hauptskript, da nichts anderes zu tun ist).
- Wenn der Versuch stattdessen erfolgreich ist, führt die Subshell die gewünschte Aufgabe aus.
Hier sind meine Fragen:
- Warum muss das Skript eine Kopie seines eigenen Inhalts an einen von der Subshell geerbten Dateideskriptor umleiten, anstatt beispielsweise den Inhalt einer anderen Datei? (Ich habe versucht, von einer anderen Datei umzuleiten und wie oben beschrieben erneut auszuführen, und die Ausführungsreihenfolge hat sich geändert: Die nicht im Hintergrund befindliche Task hat die Sperre vor der im Hintergrund befindlichen erhalten. Wenn Sie also den eigenen Inhalt der Datei verwenden, vermeiden Sie Rennbedingungen. Aber wie?)
- Warum muss das Skript überhaupt eine Kopie des Inhalts einer Datei an einen Dateideskriptor umleiten, der von der Subshell geerbt wird?
- Warum verhindert das Halten einer exklusiven Sperre für den Dateideskriptor
0
in einer Shell, dass eine Kopie desselben Skripts, die in einer anderen Shell ausgeführt wird, eine exklusive Sperre für den Dateideskriptor erhält0
? Nicht Schalen haben ihre eigenen, separaten Kopien der Standard - Datei - Deskriptoren (0
,1
, und2
, also STDIN, STDOUT und STDERR)?
linux
shell-script
io-redirection
subshell
lock
sampablokuper
quelle
quelle
Antworten:
Sie können eine beliebige Datei verwenden, sofern alle Kopien des Skripts dieselbe verwenden. Mit "
$0
Nur" wird die Sperre an das Skript selbst gebunden: Wenn Sie das Skript kopieren und für eine andere Verwendung ändern, müssen Sie keinen neuen Namen für die Sperrdatei festlegen. Das ist praktisch.Wenn das Skript über einen Symlink aufgerufen wird, befindet sich die Sperre in der eigentlichen Datei und nicht in der Verknüpfung.
(Wenn ein Prozess das Skript ausführt und es als nulltes Argument anstelle des tatsächlichen Pfads als erfundenen Wert ausgibt, bricht dies natürlich ab. Dies wird jedoch selten durchgeführt.)
Sind Sie sicher, dass dies an der verwendeten Datei und nicht nur an zufälligen Variationen lag? Wie bei einer Pipeline gibt es keine Möglichkeit, sicherzugehen, in welcher Reihenfolge die Befehle ausgeführt werden
cmd1 & cmd
. Es liegt hauptsächlich am OS-Scheduler. Ich erhalte zufällige Abweichungen von meinem System.Es sieht so aus, als ob dies so ist, dass die Shell selbst eine Kopie der Dateibeschreibung enthält, die die Sperre enthält, anstatt nur das
flock
Dienstprogramm, das sie enthält. Eine mit gemachte Sperreflock(2)
wird freigegeben, wenn die Dateideskriptoren, die sie haben, geschlossen werden.flock
Es stehen zwei Modi zur Verfügung, entweder um eine Sperre auf der Grundlage eines Dateinamens zu setzen und einen externen Befehl auszuführen (in diesem Fall wirdflock
der erforderliche offene Dateideskriptor gespeichert) oder um einen Dateideskriptor von außen zu speichern, sodass ein externer Prozess für das Speichern verantwortlich ist es.Beachten Sie, dass der Inhalt der Datei hier nicht relevant ist und keine Kopien erstellt werden. Die Umleitung zur Subshell kopiert keine Daten in sich selbst, sondern öffnet lediglich ein Handle für die Datei.
Ja, aber die Sperre bezieht sich auf die Datei , nicht auf den Dateideskriptor. Es kann immer nur eine geöffnete Instanz der Datei die Sperre halten.
Ich denke, Sie sollten in der Lage sein, dasselbe ohne die Subshell zu tun, indem Sie
exec
ein Handle für die Sperrdatei öffnen:quelle
{ }
statt( )
würde auch funktionieren und die Unterschale vermeiden.exec
.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:
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.
<$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.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.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 verwendenSie können die gleiche Abfolge von Operationen in der Shell erhalten, wenn Sie die Umleitung mit dem
exec
eingebauten Befehl ausführen.Das Skript könnte einen anderen Dateideskriptor verwenden, wenn weiterhin auf die ursprüngliche Standardeingabe zugegriffen werden soll.
oder mit einer Unterschale:
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.quelle
Die zum Sperren verwendete Datei ist unwichtig, das Skript verwendet sie,
$0
da es sich um eine Datei handelt, von der bekannt ist, dass sie existiert.Die Reihenfolge, in der die Sperren aktiviert werden, ist mehr oder weniger zufällig, je nachdem, wie schnell Ihr Computer die beiden Aufgaben starten kann.
Sie können einen beliebigen Dateideskriptor verwenden, nicht unbedingt 0. Die Sperre gilt für die Datei , die für den Dateideskriptor geöffnet wurde, nicht für den Deskriptor selbst.
quelle